www.pudn.com > VSer.rar > main.c


/*++ 
 
Copyright (c) 2005  Changzhi Zhou All Rights Reserved 
 
Module Name: 
 
    main.c 
 
Abstract: 
 
    This module is main module that deals with 
	DriverEntry, AddDevice, Unload, handles to device name. 
	SampleIoCrement and SampleIoDecrement. 
 
Environment: 
 
    Kernel mode 
 
Revision History: 
 
	Changzhi Zhou Dec 20  2004 
 
--*/ 
#include  
#include  
#include  
 
#include "main.h" 
#include "..\inc\wdmioctl.h" 
#include  
#include  
 
 
#ifdef ALLOC_PRAGMA 
#pragma alloc_text (INIT, DriverEntry) 
#pragma alloc_text (PAGE, AddDevice) 
#pragma alloc_text (PAGE, SamplePnpDispatch) 
#pragma alloc_text (PAGE, Unload) 
#endif 
 
NTSTATUS 
DriverEntry( 
    IN PDRIVER_OBJECT  DriverObject, 
    IN PUNICODE_STRING RegistryPath 
    ) 
/*++ 
 
Routine Description: 
 
    Installable driver initialization entry point. 
    This entry point is called directly by the I/O system. 
 
Arguments: 
 
    DriverObject - pointer to the driver object 
 
    RegistryPath - pointer to a unicode string representing the path, 
                   to driver-specific key in the registry. 
 
Return Value: 
 
    STATUS_SUCCESS if successful, 
    STATUS_UNSUCCESSFUL otherwise. 
 
--*/ 
{	 
	NTSTATUS            status; 
 
    UNREFERENCED_PARAMETER (RegistryPath); 
 
    DbgPrint("------  VirtualSerial Build on %s %s -------\n", __DATE__, __TIME__ ); 
 
	status = STATUS_SUCCESS; 
 
    DriverObject->MajorFunction[IRP_MJ_PNP]				= SamplePnpDispatch; 
    DriverObject->MajorFunction[IRP_MJ_POWER]			= SamplePowerDispatch; 
    DriverObject->MajorFunction[IRP_MJ_CREATE]			= SampleCreate; 
    DriverObject->MajorFunction[IRP_MJ_CLOSE]			= SampleClose; 
	DriverObject->MajorFunction[IRP_MJ_CLEANUP ]		= SampleCleanup; 
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]	= SampleIoControl; 
 
	DriverObject->MajorFunction[IRP_MJ_WRITE]           = SampleWrite; 
	DriverObject->MajorFunction[IRP_MJ_READ]            = SampleRead; 
 
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ToasterSystemControl; 
 
    DriverObject->DriverExtension->AddDevice			= AddDevice; 
    DriverObject->DriverUnload							= Unload; 
 
    return status; 
} 
 
 
 
NTSTATUS 
AddDevice( 
    IN PDRIVER_OBJECT DriverObject, 
    IN PDEVICE_OBJECT PhysicalDeviceObject 
    ) 
/*++ 
 
Routine Description: 
 
    The Plug & Play subsystem is handing us a brand new PDO, for which we 
    (by means of INF registration) have been asked to provide a driver. 
 
    We need to determine if we need to be in the driver stack for the device. 
    Create a function device object to attach to the stack 
    Initialize that device object 
    Return status success. 
 
    Remember: We can NOT actually send ANY non pnp IRPS to the given driver 
    stack, UNTIL we have received an IRP_MN_START_DEVICE. 
 
Arguments: 
 
    DeviceObject - pointer to a device object. 
 
    PhysicalDeviceObject -  pointer to a device object created by the 
                            underlying bus driver. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
    NTSTATUS                status = STATUS_SUCCESS; 
    PDEVICE_OBJECT          deviceObject = NULL; 
    PDEVICE_EXTENSION       deviceExtension; 
 
 
	UNICODE_STRING		deviceObjName; 
 
    PAGED_CODE (); 
	DbgPrint("------- AddDevice  build on %s %s ---------\n", __DATE__, __TIME__); 
 
	if( FALSE == InitializeSerialDevName( &deviceObjName ) ) 
		return STATUS_INSUFFICIENT_RESOURCES; 
    status = IoCreateDevice (DriverObject, 
                             sizeof (DEVICE_EXTENSION), 
                             &deviceObjName, 
                             FILE_DEVICE_SERIAL_PORT, 
                             FILE_DEVICE_SECURE_OPEN, 
                             FALSE, 
                             &deviceObject); 
    if (!NT_SUCCESS (status)) { 
		DebugPrint(("IoCreateDevice failed\n")); 
		if ( deviceObjName.Buffer != NULL ) 
			ExFreePool ( deviceObjName.Buffer ); 
        return status; 
    } 
 
    DebugPrint (("AddDevice PDO (0x%x) FDO (0x%x)\n", PhysicalDeviceObject, deviceObject)); 
 
    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension; 
	deviceExtension->DeviceName.MaximumLength = deviceObjName.MaximumLength; 
	deviceExtension->DeviceName.Length	= deviceObjName.Length; 
	deviceExtension->DeviceName.Buffer = deviceObjName.Buffer; 
	deviceExtension->CreatedSerialCommEntry = FALSE; 
	deviceExtension->CreatedSymbolicLink	= FALSE; 
 
	// for working thread 
	KeInitializeSpinLock( &deviceExtension->ThreadSpinLock ); 
 
    deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack ( 
                                       deviceObject, 
                                       PhysicalDeviceObject); 
    if(NULL == deviceExtension->NextLowerDriver) { 
 
        IoDeleteDevice(deviceObject); 
        return STATUS_UNSUCCESSFUL; 
    } 
    DebugPrint(("AddDevice: %x to %x->%x \n", deviceObject,  
                       deviceExtension->NextLowerDriver, 
                       PhysicalDeviceObject)); 
    // Initialize the remove event to not-signaled. 
    // 
    KeInitializeEvent(&deviceExtension->RemoveEvent,  
                      SynchronizationEvent,  
                      FALSE); 
    // 
    // Initialize the stop event to signaled. 
    // This event is signaled when the OutstandingIO becomes 1 
    // 
    KeInitializeEvent(&deviceExtension->StopEvent,  
                      SynchronizationEvent,  
                      TRUE); 
 
    deviceExtension->OutStandingIO = 1; 
	deviceExtension->bIsOpen		= FALSE; 
     
	KeInitializeSpinLock( &deviceExtension->IOCountLock ); 
	if ( PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) { 
        deviceObject->Flags |= DO_POWER_PAGABLE; 
    } 
 
    deviceObject->Flags |= DO_DIRECT_IO; 
    deviceExtension->Self = deviceObject; 
 
	// Initialize serial-related parameters 
	deviceExtension->WaitOnMaskIrp	= NULL; 
	deviceExtension->CurrentBaud	= 0; 
	deviceExtension->PendingReadIrp	= NULL; 
	KeInitializeTimer ( &deviceExtension->ReadTimer ); 
	KeInitializeDpc ( &deviceExtension->ReadDpc, ReadDpcRoutine, (PVOID)deviceExtension ); 
	 
	RtlZeroMemory( &deviceExtension->SerialStatus, sizeof( SERIAL_STATUS ) ); 
	// initialize  members relevant to TDI. 
	deviceExtension->RxBuffer	= ExAllocatePoolWithTag( NonPagedPool, RINGBUFFER_SIZE, 'RBuf' ); 
	if ( deviceExtension->RxBuffer == NULL ) 
		goto Error_Exit; 
 
	deviceExtension->lpRead	= deviceExtension->RxBuffer; 
	deviceExtension->lpRx	= deviceExtension->RxBuffer; 
 
	deviceExtension->hTransAddr	= NULL; 
	deviceExtension->lpTransAddrFileObject	= NULL; 
	deviceExtension->TDILowerDeviceObject	= NULL; 
	deviceExtension->RemoteAddress			= 0x601a8c0;	// 192.168.1.6 
	deviceExtension->RemotePort				= 0x7217;		// 6002 
	RtlZeroMemory( (PVOID)&deviceExtension->recvContext, sizeof( RECV_CONTEXT ) ); 
 
	deviceExtension->recvContext.RemainderBuffer = ExAllocatePoolWithTag ( NonPagedPool, RECVREMAINDER_BUFFER_SIZE, 'Rrem' ); 
	if ( deviceExtension->recvContext.RemainderBuffer == NULL ) 
		goto Error_Exit; 
    KeInitializeEvent(&deviceExtension->recvContext.Event,  
                      SynchronizationEvent, 
                      FALSE); 
	RtlZeroMemory ( (PVOID)&deviceExtension->recvContext.ReceiveDatagramInfo, sizeof ( TDI_CONNECTION_INFORMATION ) ); 
	RtlZeroMemory ( (PVOID)&deviceExtension->recvContext.ReturnInfo, sizeof ( TDI_CONNECTION_INFORMATION ) ); 
 
    INITIALIZE_PNP_STATE(deviceExtension); 
  
	//++ Register device interface for win32app 
	status = IoRegisterDeviceInterface( PhysicalDeviceObject, 
					(LPGUID)&GUID_CLASS_COMPORT, 
					NULL, 
					&deviceExtension->InterfaceName ); 
	DebugPrint(("InterfaceName:  %ws\n", deviceExtension->InterfaceName.Buffer )); 
	if( !NT_SUCCESS ( status ) ){ 
		DebugPrint(( "AddDevice:  IoRegisterDeviceInterface failed (%x) \n", status )); 
		goto Error_Exit; 
	} 
	//-- 
 
    deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 
 
    return STATUS_SUCCESS; 
Error_Exit: 
	if( deviceExtension->RxBuffer  ) 
	{ 
		ExFreePool ( deviceExtension->RxBuffer ); 
		deviceExtension->RxBuffer = NULL; 
	} 
	if ( deviceExtension->recvContext.RemainderBuffer ) 
	{ 
		ExFreePool ( deviceExtension->recvContext.RemainderBuffer ); 
		deviceExtension->recvContext.RemainderBuffer = NULL; 
	} 
 
	IoDetachDevice( deviceExtension->NextLowerDriver ); 
	IoDeleteDevice( deviceObject ); 
	return status; 
 
} 
 
NTSTATUS 
SerialDoExternalNaming( 
	IN PDEVICE_EXTENSION	deviceExtension, 
	IN LONG					ComX 
	) 
/*++ 
 
Routine Description: 
 
	Create external name, SymbolicLinkName and DOS name. 
 
Arguments: 
 
    deviceExtension - pointer to a device object extension. 
 
    ComX -  indicates the number of Serial Ports. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	NTSTATUS		status; 
	ULONG			bufLen; 
	WCHAR			ComXBuffer[ 4 ]; 
	UNICODE_STRING	instanceStr; 
	DebugPrint(("Enter SerialdoExternalNaming routine...\n")); 
	if( deviceExtension->CreatedSymbolicLink || deviceExtension->CreatedSerialCommEntry ){ 
		DebugPrint(("Already create symboliclink or serial commentry\n")); 
		return STATUS_UNSUCCESSFUL; 
	} 
	ASSERT( deviceExtension->SymbolicLinkName.Buffer == NULL ); 
	ASSERT( deviceExtension->DosName.Buffer == NULL ); 
 
	RtlInitUnicodeString(&instanceStr, NULL); 
	instanceStr.MaximumLength = sizeof( ComXBuffer ); 
	instanceStr.Buffer = ComXBuffer;	// 4 WCHAR 
	RtlIntegerToUnicodeString( ComX, 10, &instanceStr ); 
 
	// 将SymbolicLinkName设置为 \DosDevices\COMn 的形式。其中n = ComX 
	RtlZeroMemory( &deviceExtension->SymbolicLinkName, sizeof( UNICODE_STRING ) ); 
	deviceExtension->SymbolicLinkName.MaximumLength = DEVICE_OBJECT_NAME_LENGTH + sizeof( WCHAR ); 
	deviceExtension->SymbolicLinkName.Buffer = ExAllocatePoolWithTag( PagedPool, deviceExtension->SymbolicLinkName.MaximumLength, 'SymL' ); 
	if( deviceExtension->SymbolicLinkName.Buffer == NULL ){ 
		DebugPrint(("SERIAL: Couldn't allocate memory for symbolic link name\n")); 
		status = STATUS_INSUFFICIENT_RESOURCES; 
		goto SerialDoExternalNamingError; 
	} 
	RtlZeroMemory( deviceExtension->SymbolicLinkName.Buffer, deviceExtension->SymbolicLinkName.MaximumLength ); 
	RtlAppendUnicodeToString( &deviceExtension->SymbolicLinkName, DOS_DEVICE_NAME );	//L"\\DosDevices\\COM" 
	RtlAppendUnicodeStringToString( &deviceExtension->SymbolicLinkName, &instanceStr); 
	DebugPrint(("SymbolicLinkName:   %wZ\n",  deviceExtension->SymbolicLinkName )); 
 
	// 将DosName初始化 COMn 的形式, 其中 n = ComX 
	deviceExtension->DosName.MaximumLength = 64 + sizeof(WCHAR); 
	deviceExtension->DosName.Buffer = ExAllocatePoolWithTag( PagedPool, 64 + sizeof( WCHAR ), 'Name' ); 
	if( deviceExtension->DosName.Buffer == NULL ){ 
		DebugPrint(("SERIAL: Couldn't allocate memory for Dos name\n")); 
		status =  STATUS_INSUFFICIENT_RESOURCES; 
		goto SerialDoExternalNamingError; 
	} 
	deviceExtension->DosName.Length = 0; 
	RtlZeroMemory( deviceExtension->DosName.Buffer, deviceExtension->DosName.MaximumLength ); 
	RtlAppendUnicodeToString( &deviceExtension->DosName, L"COM" ); 
	RtlAppendUnicodeStringToString( &deviceExtension->DosName, &instanceStr); 
	DebugPrint(("DosName:   %wZ\n", &deviceExtension->DosName )); 
	DebugPrint(("DeviceName:    %wZ\n", &deviceExtension->DeviceName )); 
 
	// 生成符号连接,至此本设备对win32应用表现的Dos设备名为 \DosDevices\COMn 
	status = IoCreateSymbolicLink( &deviceExtension->SymbolicLinkName, &deviceExtension->DeviceName ); 
	if( !NT_SUCCESS( status )){ 
		DebugPrint(("SERIAL:  Couldn't create the symbolic link with error %d\n", status)); 
		goto SerialDoExternalNamingError; 
	}else{ 
		DebugPrint(("Create the symbolic link OK\n")); 
		deviceExtension->CreatedSymbolicLink = TRUE; 
	} 
	// 在注册表的 HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM 中,添加ComX的键值 
	// 若不进行这一步,则超级终端程序无法检测到本虚拟串口设备 
	status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, 
		deviceExtension->DeviceName.Buffer, REG_SZ, 
		deviceExtension->DosName.Buffer, 
		deviceExtension->DosName.Length + sizeof(WCHAR)); 
	if( !NT_SUCCESS( status )){ 
		DebugPrint(("SERIAL: Couldn't create the device map entry\n------- for port %wZ\n", deviceExtension->DeviceName )); 
		goto SerialDoExternalNamingError; 
	} 
	deviceExtension->ComX = ComX; 
	deviceExtension->CreatedSerialCommEntry = TRUE; 
 
SerialDoExternalNamingError: 
 
	if( !NT_SUCCESS( status )){ 
		if( deviceExtension->DosName.Buffer != NULL ){ 
			ExFreePool( deviceExtension->DosName.Buffer ); 
			deviceExtension->DosName.Buffer = NULL; 
			deviceExtension->CreatedSerialCommEntry = FALSE; 
		} 
		if( deviceExtension->CreatedSymbolicLink ){ 
			IoDeleteSymbolicLink( &deviceExtension->SymbolicLinkName ); 
			deviceExtension->CreatedSymbolicLink = FALSE; 
		} 
		if( deviceExtension->SymbolicLinkName.Buffer != NULL ){ 
			ExFreePool( deviceExtension->SymbolicLinkName.Buffer ); 
			deviceExtension->SymbolicLinkName.Buffer = NULL; 
		} 
	} 
	return status; 
} 
 
 
BOOLEAN InitializeSerialDevName( PUNICODE_STRING lpDeviceName ) 
/*++ 
 
Routine Description: 
 
	Initialize serial device name. This name is available in Kernel Mode. 
 
Arguments: 
 
    PUNICODE_STRING lpDeviceName - the pointer to device name string. 
 
Return Value: 
 
    return TRUE if it is successful, otherwise return FALSE. 
 
--*/ 
{ 
 
	WCHAR				instanceNumberBuffer[ 4 ]; 
	UNICODE_STRING		instanceStr; 
	static ULONG				currentInstance = 10; 
	// 首先处理deviceObjName,这是一个核心态使用的设备名,形如"\\Device\\SiSerial0" 
	// Zero out allocated memory pointers so we know if they must be freed 
	RtlZeroMemory( lpDeviceName, sizeof(UNICODE_STRING) ); 
	lpDeviceName->MaximumLength = DEVICE_OBJECT_NAME_LENGTH * sizeof(WCHAR); 
	lpDeviceName->Buffer = ExAllocatePool(PagedPool, lpDeviceName->MaximumLength + sizeof(WCHAR)); 
	if ( lpDeviceName->Buffer == NULL) { 
		DebugPrint(("Couldn't allocate memory for device name\n")); 
		return FALSE; 
	} 
	RtlZeroMemory( lpDeviceName->Buffer, lpDeviceName->MaximumLength + sizeof(WCHAR)); 
	// now, the size of deviceObjName.Buffer is (128 + 1)(WCHAR) 
	RtlAppendUnicodeToString( lpDeviceName, DEVICE_NAME);	// L"\\Device\\VSer" 
	 
	// 处理 lpDeviceName 的后缀序号 
	RtlInitUnicodeString(&instanceStr, NULL); 
	instanceStr.MaximumLength	= sizeof(instanceNumberBuffer); 
	instanceStr.Buffer			= instanceNumberBuffer;	// 20 WCHAR 
	currentInstance++; 
	RtlIntegerToUnicodeString( currentInstance, 10, &instanceStr); 
	RtlAppendUnicodeStringToString( lpDeviceName, &instanceStr); 
	DebugPrint(("DeviceName:\n")); 
	DebugPrint(("----------- %ws\n", lpDeviceName->Buffer )); 
	return TRUE; 
} 
 
NTSTATUS 
SerialUndoExternalNaming( 
	IN PDEVICE_EXTENSION	deviceExtension 
	) 
/*++ 
 
Routine Description: 
 
	Delete or unregister external name, SymbolicLinkName and DOS name. 
 
Arguments: 
 
    deviceExtension - pointer to a device object extension. 
 
Return Value: 
 
    NT status code. 
 
--*/ 
{ 
	NTSTATUS	status = STATUS_SUCCESS; 
	if( deviceExtension->CreatedSymbolicLink ){ 
		IoDeleteSymbolicLink( &deviceExtension->SymbolicLinkName ); 
		ExFreePool( deviceExtension->SymbolicLinkName.Buffer ); 
		deviceExtension->SymbolicLinkName.Buffer = NULL; 
		deviceExtension->CreatedSymbolicLink = FALSE; 
	}else{ 
		ASSERT( deviceExtension->SymbolicLinkName.Buffer == NULL ); 
	} 
	ASSERT( deviceExtension->DeviceName.Buffer != NULL ); 
	if( deviceExtension->CreatedSerialCommEntry ){ 
		status = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, deviceExtension->DeviceName.Buffer ); 
		if( !NT_SUCCESS( status ) ){ 
			DebugPrint(("RtlDeleteRegistryValue device map failed\n")); 
		} 
		ExFreePool( deviceExtension->DosName.Buffer ); 
		deviceExtension->DosName.Buffer = NULL; 
		deviceExtension->CreatedSerialCommEntry = FALSE; 
	}else{ 
		ASSERT( deviceExtension->DosName.Buffer == NULL ); 
	} 
	return status; 
} 
 
NTSTATUS 
SerialRemoveDevObj(IN PDEVICE_OBJECT PDevObj) 
/*++ 
 
Routine Description: 
 
    Removes a serial device object from the system. 
 
Arguments: 
 
    PDevObj - A pointer to the Device Object we want removed. 
 
Return Value: 
 
    Always TRUE 
 
--*/ 
{ 
	PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension; 
	PAGED_CODE(); 
	 
	DebugPrint(("SERIAL: Enter SerialRemoveDevObj\n")); 
	 
	 
	// 
	// Free memory allocated in the extension 
	// 
	 
	if (pDevExt->DeviceName.Buffer != NULL) { 
		ExFreePool(pDevExt->DeviceName.Buffer); 
	} 
	 
	if (pDevExt->SymbolicLinkName.Buffer != NULL) { 
		ExFreePool(pDevExt->SymbolicLinkName.Buffer); 
	} 
	 
	if (pDevExt->DosName.Buffer != NULL) { 
		ExFreePool(pDevExt->DosName.Buffer); 
	} 
	DebugPrint(("SERIAL: Leave SerialRemoveDevObj\n")); 
 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS CompleteRequest(IN PIRP Irp, IN NTSTATUS status, IN ULONG info) 
{ 
    Irp->IoStatus.Status = status; 
    Irp->IoStatus.Information = info; 
    IoCompleteRequest(Irp, IO_NO_INCREMENT); 
    return status; 
} 
     
VOID 
Unload( 
    IN PDRIVER_OBJECT DriverObject 
    ) 
/*++ 
 
Routine Description: 
 
    Free all the allocated resources in DriverEntry, etc. 
 
Arguments: 
 
    DriverObject - pointer to a driver object. 
 
Return Value: 
 
    VOID. 
 
--*/ 
{ 
    PAGED_CODE (); 
 
    // 
    // The device object(s) should be NULL now 
    // (since we unload, all the devices objects associated with this 
    // driver must be deleted. 
    // 
	DebugPrint( ("Is unloading......\n") ); 
    ASSERT(DriverObject->DeviceObject == NULL); 
     
    // 
    // We should not be unloaded until all the devices we control  
    // have been removed from our queue.   
    // 
    DebugPrint (("Unload:  unload\n")); 
 
    return; 
} 
 
LONG 
SampleIoIncrement( 
    IN OUT PDEVICE_EXTENSION DeviceExtension 
    ) 
/* ++ 
  
Routine Description: 
 
	Increment deviceExtension->OutStandingIO within the scope of IOCountLock. 
 
Arguments: 
 
	deviceExtension - pointer to a device object extension. 
 
Return Value: 
 
	deviceExtension->OutStandingIO 
 
--*/ 
{ 
    LONG  result = 0; 
    KIRQL oldIrql; 
 
    KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql); 
 
    result = InterlockedIncrement(&DeviceExtension->OutStandingIO); 
 
    // 
    // when OutStandingIO bumps from 1 to 2, clear the StopEvent 
    // 
 
    if(result == 2) { 
 
        KeClearEvent(&DeviceExtension->StopEvent); 
    } 
 
    KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql); 
	DebugPrint(("SampleIncrement: %d\n", result)); 
 
    return result; 
} 
 
LONG 
SampleIoDecrement( 
    IN OUT PDEVICE_EXTENSION DeviceExtension 
    ) 
/*++ 
  
Routine Description: 
	 
	  Decrement deviceExtension->OutStandingIO within the scope of IOCountLock. 
 
Arguments: 
 
	deviceExtension - pointer to a device object extension. 
 
Return Value: 
 
	deviceExtension->OutStandingIO 
 
--*/ 
{ 
    LONG  result = 0; 
    KIRQL oldIrql; 
 
    KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql); 
 
    result = InterlockedDecrement(&DeviceExtension->OutStandingIO); 
 
    if(result == 1) { 
 
        KeSetEvent(&DeviceExtension->StopEvent, IO_NO_INCREMENT, FALSE); 
    } 
 
    if(result == 0) { 
 
        ASSERT(Removed == DeviceExtension->DevicePnPState); 
 
        KeSetEvent(&DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE); 
    } 
 
    KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql); 
	DebugPrint(("SampleIoDecrement: %d\n", result)); 
 
 
    return result; 
}