// Main program for PN31Snoop driver
// Generated by Walt Oney's driver wizard


#include "../Common/Interface.h"
#include "stddcls.h"
#include "driver.h"
#include "MyKdPrint.h"
#include "buffer.h"

NTSTATUS	AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo);
VOID		DriverUnload(IN PDRIVER_OBJECT fido);
NTSTATUS	DispatchAny(IN PDEVICE_OBJECT fido, IN PIRP Irp);
NTSTATUS	DispatchPower(IN PDEVICE_OBJECT fido, IN PIRP Irp);
NTSTATUS	DispatchPnp(IN PDEVICE_OBJECT fido, IN PIRP Irp);
NTSTATUS	DispatchWmi(IN PDEVICE_OBJECT fido, IN PIRP Irp);
NTSTATUS	MyDispatchInternalIOCTL(IN PDEVICE_OBJECT fido, IN PIRP Irp);
void		DumpURB(struct Buffer *b, PURB pUrb, BOOLEAN bReturnedFromHCD);

NTSTATUS	DispatchCreateClose(IN PDEVICE_OBJECT fido, IN PIRP Irp);
NTSTATUS	DispatchInternalIOCTL(IN PDEVICE_OBJECT fido, IN PIRP Irp);


// functions to handle conversion from PipeHandle to endpint number
struct ENDPOINT_INFO 
{
    USBD_PIPE_HANDLE PipeHandle;
	unsigned char    Endpoint;
};

// the number of endpoint that can be handled is defined here.
// if you use a device with more endpoints, they won't be decoded,
// but packets will be logged anyway.

struct ENDPOINT_INFO TabEndpointInfo[MAX_ENDPOINT] =
{
	{ NULL, 0 }
};

BOOLEAN GetEndpointInfo(USBD_PIPE_HANDLE inPipeHandle, unsigned char * outEndpoint)
{
	int i;

	for (i=0;i<arraysize(TabEndpointInfo);i++)
	{
		if (TabEndpointInfo[i].PipeHandle != NULL 
			&& TabEndpointInfo[i].PipeHandle == inPipeHandle)
		{
			*outEndpoint = TabEndpointInfo[i].Endpoint;
			return TRUE;
		}
	}

	return FALSE;
}

void AddEndpointInfo(USBD_PIPE_HANDLE inPipeHandle, unsigned char inEndpoint)
{
	int i;

	// search for an existing PipeHandle
	for (i=0;i<arraysize(TabEndpointInfo);i++)
	{
		if (TabEndpointInfo[i].PipeHandle != NULL 
			&& TabEndpointInfo[i].PipeHandle == inPipeHandle)
		{
			// if found, replace the Endpoint information
			TabEndpointInfo[i].Endpoint = inEndpoint;
			return ; 
		}
	}

	// search for a free slot 
	for (i=0;i<arraysize(TabEndpointInfo);i++)
	{
		if (TabEndpointInfo[i].PipeHandle == NULL)
		{
			TabEndpointInfo[i].PipeHandle = inPipeHandle;
			TabEndpointInfo[i].Endpoint = inEndpoint;
			return ;
		}
	}

	LogPrintf("AddEndpointInfo failed!\n");
}



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

void Sleep(int nsecond)
{
	LARGE_INTEGER li;

	li.QuadPart = - nsecond * SECONDS;

	KeDelayExecutionThread(KernelMode,FALSE,&li);
}


#pragma INITCODE

/*
	DriverEntry : this function is called by Windows whenever this driver is
		loaded in memory, this is the only entry point this is accessible for
		the OS. Other entry points will be set up inside DriverEntry().

	From the Windows 2000 DDK : DriverEntry routines are called in the context
	of a system thread at IRQL PASSIVE_LEVEL.
*/

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
								IN PUNICODE_STRING RegistryPath)
{
	UCHAR MajorVersion, MinorVersion;
	int i;
	char msg[] = "PN31Snoop compiled on 10 " __DATE__ " " __TIME__ " loading\n";

	LogInit();

	DbgPrint(msg);
	LogPrintf(msg);

	// Insist that OS support at least the WDM 1.0 (Win98 DDK)
	
	if (!IoIsWdmVersionAvailable(1, 0))
	{
		LogPrintf("PN31Snoop - Expected version of WDM (%d.%d) not available\n", 1, 0);
		return STATUS_UNSUCCESSFUL;
	}
	
	// See if we're running under Win98 or NT:
	MajorVersion = 0;
	MinorVersion = 0;

	while (IoIsWdmVersionAvailable(MajorVersion,MinorVersion))
		MajorVersion ++;

	MajorVersion --;

	while (IoIsWdmVersionAvailable(MajorVersion,MinorVersion))
		MinorVersion ++;
	MinorVersion --;

	// Initialize function pointers
	DriverObject->DriverUnload = DriverUnload;
	DriverObject->DriverExtension->AddDevice = AddDevice;
	
	for (i = 0; i < arraysize(DriverObject->MajorFunction); ++i)
		DriverObject->MajorFunction[i] = DispatchAny;

	// POWER IRP needs a special treatment
	DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;

//    DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
//    DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose;
//    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIOCTL;
//    DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = DispatchInternalIOCTL;

	// we need a special callback to call RemoveDevice().
	// this is now done in MyDispatchPnp
	// DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp;
    // Initialize the global stuff
//    RtlZeroMemory(&GlobalData, sizeof(GlobalData));
//    return CreateControllerObject(DriverObject);

	return STATUS_SUCCESS;
}

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

#pragma PAGEDCODE

/*
	DriverUnload() is called whenever our driver is unloaded from memory.
	From Windows 2000 DDK : run at PASSIVE_LEVEL, as are any driver-created
	system threads. 
*/

VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
	char msg [] = "Driver unloaded!\n";

	LogPrintf("PN31Snoop - DriverUnload(%p) : DriverObject %p, IRQL=%d\n",
		DriverUnload,DriverObject,KeGetCurrentIrql());

//	DumpDriverObject(DriverObject);

	LogPrintf(msg);
	LogDone();

	/*
		let the time for the thread to stop. It's no longer needed, since we
		use an event to synchronize the destruction of the thread.
		after that, we CANNOT call LogPrintf).
	*/

	DbgPrint(msg);
}

const char * GetIrpPnpMinorFunctionName(ULONG fcn)
{
	static const char* fcnname[] =
	{
		"IRP_MN_START_DEVICE",
		"IRP_MN_QUERY_REMOVE_DEVICE",
		"IRP_MN_REMOVE_DEVICE",
		"IRP_MN_CANCEL_REMOVE_DEVICE",
		"IRP_MN_STOP_DEVICE",
		"IRP_MN_QUERY_STOP_DEVICE",
		"IRP_MN_CANCEL_STOP_DEVICE",
		"IRP_MN_QUERY_DEVICE_RELATIONS",
		"IRP_MN_QUERY_INTERFACE",
		"IRP_MN_QUERY_CAPABILITIES",
		"IRP_MN_QUERY_RESOURCES",
		"IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
		"IRP_MN_QUERY_DEVICE_TEXT",
		"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
		"",
		"IRP_MN_READ_CONFIG",
		"IRP_MN_WRITE_CONFIG",
		"IRP_MN_EJECT",
		"IRP_MN_SET_LOCK",
		"IRP_MN_QUERY_ID",
		"IRP_MN_QUERY_PNP_DEVICE_STATE",
		"IRP_MN_QUERY_BUS_INFORMATION",
		"IRP_MN_DEVICE_USAGE_NOTIFICATION",
		"IRP_MN_SURPRISE_REMOVAL",
	};

	if (0<=fcn && fcn<arraysize(fcnname))
		return fcnname[fcn];

	return NULL;
}

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

#pragma PAGEDCODE

VOID RemoveDevice(IN PDEVICE_OBJECT fido)
{
//	PAGED_CODE();

	// since we called this function before the original one,
	// we are sure that the lower device object is still the same

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;

	LogPrintf("PN31Snoop - RemoveDevice(%p) : fido=%p pdx=%p\n",RemoveDevice,fido,pdx);
	
	if (pdx->LowerDeviceObject != NULL)
	{
		// save our modified PDRIVER_OBJECT
		PDRIVER_OBJECT d = pdx->LowerDeviceObject->DriverObject;

//		LogPrintf("  fdo=%p OriginalDriverObject=%p d=%p\n",
//			pdx->LowerDeviceObject,pdx->OriginalDriverObject,d);

		// restore the driver pointer in the PDO
		pdx->LowerDeviceObject->DriverObject = pdx->OriginalDriverObject;

		IoDetachDevice(pdx->LowerDeviceObject);

		// free our modified PDRIVER_OBJECT
		ExFreePool(d);
	}

	IoDeleteDevice(fido);
}

NTSTATUS MyDispatchPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG fcn = stack->MinorFunction;
	PDEVICE_OBJECT fido;
//	NTSTATUS status;
	PDEVICE_EXTENSION pdx;
	
	/*
	const char * MinorFunctionName = GetIrpPnpMinorFunctionName(fcn);
	if (MinorFunctionName != NULL)
		LogPrintf("PN31Snoop - MyDispatchPNP(%p) : IRP_MJ_PNP (%s)\n",
			MyDispatchPnp,MinorFunctionName);
	else
		LogPrintf("PN31Snoop - MyDispatchPNP(%p) : IRP_MJ_PNP (0x%x)\n",
			MyDispatchPnp,fcn);
			*/

	
	fido = fdo->AttachedDevice;
//	LogPrintf("fido=%p\n",fido);

	pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
//	LogPrintf("pdx=%p\n",pdx);

	// save the original function to call

	PDRIVER_DISPATCH pfn = pdx->OriginalDriverObject->MajorFunction[IRP_MJ_PNP];

	// the following lines should be executed ONLY if DispatchPNP() callback
	// is not used, since it duplicates its work.

	// They should also be executed BEFORE calling the original Dispatch function
	// since this original Dispatch function will surely destroy its DeviceObject.

	if (fcn == IRP_MN_REMOVE_DEVICE)
		RemoveDevice(fido);
	
	return pfn(fdo,Irp);	
}

/*
	Experimentally, under Windows 2000, run at IRQL DISPATCH_LEVEL,
	which is not enought for us, since ZwWriteFile() must be called
	at IRQL PASSIVE_LEVEL
*/

VOID gDeferredRoutine(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )
{

	KIRQL currentIrql = KeGetCurrentIrql();
	DbgPrint("gDeferredRoutine : currentIrql=%d\n",currentIrql);
}

/*
	AddDevice is called by Windows for each device we have to managed. This is
	where we get the oportunity to be added in the device object stack.

  From the Windows 2000 DDK : An AddDevice routine is called in the context of
  a system thread at IRQL PASSIVE_LEVEL.

  A device driver can register a CustomDpc routine by calling KeInitializeDpc
  after creating a device object. The driver should make this call from its
  AddDevice routine. Callers of this routine must be running at
  IRQL PASSIVE_LEVEL.
*/

NTSTATUS AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo)
{
	NTSTATUS status;
	PDEVICE_OBJECT fido, fdo;
	PDEVICE_EXTENSION pdx;
	PDRIVER_OBJECT d;

	LogPrintf("PN31Snoop - AddDevice(%p) : DriverObject %p, pdo %p\n",
		AddDevice,DriverObject, pdo);

//	DumpDriverObject(DriverObject);
//	DumpDeviceObject(pdo);

	// create a custom DPC
	//KeInitializeDpc(&gDPC,gDeferredRoutine,NULL);

	// Create a PN31Snoop device object to represent the hardware we're managing.
	
	status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),NULL,
		FILE_DEVICE_UNKNOWN, 0, FALSE, &fido);
	if (!NT_SUCCESS(status))
	{						// can't create device object
		LogPrintf("PN31Snoop - IoCreateDevice failed - %x\n", status);
		return status;
	}						// can't create device object

	// Benoit PAPILLAULT 13/07/2001
	// fido->DeviceExtension is a user define structure whose size is passed
	// to IoCreateDevice(). We can store whatever we want inside.

	pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
//	LogPrintf("  fido=%p pdx=%p\n",fido,pdx);
	
	// From this point forward, any error will have side effects that need to
	// be cleaned up. Using a try-finally block allows us to modify the program
	// easily without losing track of the side effects.
	
	__try
	{						// finish initialization
		IoInitializeRemoveLock(&pdx->RemoveLock, 0, 0, 255);
		pdx->DeviceObject = fido;
		pdx->Pdo = pdo;
		
		// Add our device object to the stack and propagate critical settings
		// from the immediately lower device object
		
		fdo = IoAttachDeviceToDeviceStack(fido, pdo);

		pdx->LowerDeviceObject = fdo;
		fido->Flags |= fdo->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO | DO_POWER_PAGABLE | DO_POWER_INRUSH);
		fido->DeviceType = fdo->DeviceType;
		fido->Characteristics = fdo->Characteristics;
		fido->AlignmentRequirement = fdo->AlignmentRequirement;
		
		// Clear the "initializing" flag so that we can get IRPs
		
		fido->Flags &= ~DO_DEVICE_INITIALIZING;

//		DumpDriverObject(DriverObject);
//		DumpDeviceObject(fdo);
//		DumpDeviceObject(fido);
//		DumpDeviceObject(pdo);

		// we make a copy of fdo->DriverObject
		d = (PDRIVER_OBJECT)ExAllocatePool(NonPagedPool,sizeof(DRIVER_OBJECT));
		if (d != NULL)
		{
			*d = *fdo->DriverObject;

			// we make some changes to this copy
			d->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = MyDispatchInternalIOCTL;
			d->MajorFunction[IRP_MJ_PNP]                     = MyDispatchPnp;

			// here is the trick : we save the original DriverObject
			// and next, it points to our modified copy
			pdx->OriginalDriverObject = fdo->DriverObject;
			fdo->DriverObject = d;

//			LogPrintf("  fdo=%p OriginalDriverObject=%p d=%p\n",
//				fdo,pdx->OriginalDriverObject,d);
		}
		else
			LogPrintf("ExAllocatePool failed : not redirecting PDO->DriverObject\n");
	}						// finish initialization
	__finally
	{						// cleanup side effects
		if (!NT_SUCCESS(status))
		{					// need to cleanup
			IoDeleteDevice(fido);
		}					// need to cleanup
	}						// cleanup side effects
	
	return status;
}

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

#pragma LOCKEDCODE

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

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

#pragma LOCKEDCODE				// make no assumptions about pageability of dispatch fcns
#include "debug.h"

NTSTATUS DispatchAny(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	NTSTATUS status;
	
	const char* irpname[] =
	{
		"IRP_MJ_CREATE",
		"IRP_MJ_CREATE_NAMED_PIPE",
		"IRP_MJ_CLOSE",
		"IRP_MJ_READ",
		"IRP_MJ_WRITE",
		"IRP_MJ_QUERY_INFORMATION",
		"IRP_MJ_SET_INFORMATION",
		"IRP_MJ_QUERY_EA",
		"IRP_MJ_SET_EA",
		"IRP_MJ_FLUSH_BUFFERS",
		"IRP_MJ_QUERY_VOLUME_INFORMATION",
		"IRP_MJ_SET_VOLUME_INFORMATION",
		"IRP_MJ_DIRECTORY_CONTROL",
		"IRP_MJ_FILE_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CONTROL",
		"IRP_MJ_INTERNAL_DEVICE_CONTROL",
		"IRP_MJ_SHUTDOWN",
		"IRP_MJ_LOCK_CONTROL",
		"IRP_MJ_CLEANUP",
		"IRP_MJ_CREATE_MAILSLOT",
		"IRP_MJ_QUERY_SECURITY",
		"IRP_MJ_SET_SECURITY",
		"IRP_MJ_POWER",
		"IRP_MJ_SYSTEM_CONTROL",
		"IRP_MJ_DEVICE_CHANGE",
		"IRP_MJ_QUERY_QUOTA",
		"IRP_MJ_SET_QUOTA",
		"IRP_MJ_PNP",
	};
	
	UCHAR type = stack->MajorFunction;
/*
	if (type == IRP_MJ_PNP)
	{
		ULONG fcn = stack->MinorFunction;
	
		const char * MinorFunctionName = GetIrpPnpMinorFunctionName(fcn);
		if (MinorFunctionName != NULL)
			LogPrintf("PN31Snoop - DispatchAny(%p) : IRP_MJ_PNP (%s)\n",
				DispatchAny,MinorFunctionName);
		else
			LogPrintf("PN31Snoop - DispatchAny(%p) : IRP_MJ_PNP (0x%x)\n",
				DispatchAny,fcn);
	}
	else
	{
		if (type >= arraysize(irpname))
			LogPrintf("PN31Snoop - DispatchAny(%p) : Unknown IRP, MajorFunction=0x%x\n",
				DispatchAny,type);
		else
			LogPrintf("PN31Snoop - DispatchAny(%p) : %s\n",DispatchAny, irpname[type]);
	}
	*/
	
	// Pass request down without additional processing
	
	status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
	if (!NT_SUCCESS(status))
		return CompleteRequest(Irp, status, 0);
	IoSkipCurrentIrpStackLocation(Irp);
//	LogPrintf("before IoCallDriver %p %p\n",pdx->LowerDeviceObject, Irp));
	status = IoCallDriver(pdx->LowerDeviceObject, Irp);
//	LogPrintf("after  IoCallDriver %p %p\n",pdx->LowerDeviceObject, Irp));
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	return status;
}

NTSTATUS MyInternalIOCTLCompletion(IN PDEVICE_OBJECT fido, IN PIRP Irp, IN PVOID inContext)
{
	struct Buffer b;

//	LogPrintf("PN31Snoop - MyInternalIOCTLCompletion(%p) : fido=%p, Irp=%p, Context=%p, IRQL=%d\n",
//		MyInternalIOCTLCompletion,fido,Irp,inContext,KeGetCurrentIrql());

//	DumpIrp(Irp);

	PCONTEXT Context = (PCONTEXT)inContext;
//	DumpContext(Context);

//	if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
//		KeInsertQueueDpc (&gDPC,NULL,NULL);

	// restoring information.

	Context->Stack->CompletionRoutine = Context->CompletionRoutine;
	Context->Stack->Context           = Context->Context;
	Context->Stack->Control           = Context->Control;

	// dumping URB 

	BufferInit(&b);
//	BufferPrintf(&b," <<<  URB %d coming back  <<< \n",Context->uSequenceNumber);
//	DumpURB(&b,Context->pUrb, TRUE);	// TODO (1er)
	PURB pUrb = Context->pUrb;
	if (pUrb->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER)
	{
		struct _URB_BULK_OR_INTERRUPT_TRANSFER *pBulkOrInterruptTransfer = (struct _URB_BULK_OR_INTERRUPT_TRANSFER *) pUrb;
		SendToPipe (PC_BULK_DOWN, pBulkOrInterruptTransfer->TransferBufferLength, (char*)pBulkOrInterruptTransfer->TransferBuffer);

		// Disable know keys 
		if ((pBulkOrInterruptTransfer->TransferBufferLength == 8) && (FindPN31Key((BYTE*)pBulkOrInterruptTransfer->TransferBuffer)))
			memset ((BYTE*)pBulkOrInterruptTransfer->TransferBuffer+1, 0, 7);

		// TODO : replace TransferBuffer 1 and 3 to replace default keys
	}

	LogBuffer(&b);
	BufferDone(&b);

	// saving some field before freeing the structure
	PVOID OldContext = Context->Context;
	PIO_COMPLETION_ROUTINE OldCompletionRoutine = Context->CompletionRoutine;

	// freeing the allocated structure
	ExFreePool(Context);

	// calling the old CompletionRoutine, if there was one

	if (OldCompletionRoutine != NULL)
		return OldCompletionRoutine(fido,Irp,OldContext);

	return STATUS_SUCCESS;
}

NTSTATUS MyDispatchInternalIOCTL(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
//	LogPrintf("PN31Snoop - MyDispatchInternalIOCTL(%p) : fdo=%p, Irp=%p, IRQL=%d\n",
//		MyDispatchInternalIOCTL,fdo,Irp,KeGetCurrentIrql());
/*
	if (KeGetCurrentIrql() == PASSIVE_LEVEL)
		DbgPrint("MyDispatchInternalIOCTL - Priority=%d\n",
			KeQueryPriorityThread(KeGetCurrentThread()));
*/
//	DumpDeviceObject(fdo);
//	DumpIrp(Irp);

//	if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
//		KeInsertQueueDpc (&gDPC,NULL,NULL);

	// we want to recover OriginalDriverObject which is stored in fido,
	// so we need to recover fido first, by the AttachedDevice field from FDO.

	PDEVICE_OBJECT fido = fdo->AttachedDevice;
//	LogPrintf("  fido = %p\n",fido));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
//	LogPrintf("  OriginalDriverObject = %p\n",pdx->OriginalDriverObject));

	// try to print the URB 

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG dwControlCode = stack->Parameters.DeviceIoControl.IoControlCode;
//	LogPrintf("  dwControlCode=%d\n",dwControlCode));
	if (dwControlCode == IOCTL_INTERNAL_USB_SUBMIT_URB)
	{
		struct Buffer b;
		ULONG uSequenceNumber = InterlockedIncrement((PLONG)&pdx->uSequenceNumber);

		BufferInit(&b);

//		BufferPrintf(&b," >>>  URB %d going down  >>> \n", uSequenceNumber);

		// we cannot call ZwWriteFile() cause it must be running at
		// IRQL_PASSIVE_LEVEL (0) and most of the time, MyDispatchInternalIOCTL
		// is running at IRQL_DISPATCH_LEVEL (2).
		// LogPrintf(" KeGetCurrentIrql() = %d\n",KeGetCurrentIrql()));

		PURB pUrb = (PURB) stack->Parameters.Others.Argument1;
//		DumpURB(&b,pUrb,FALSE);		// TODO (2ieme)
	if (pUrb->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER)
	{
		struct _URB_BULK_OR_INTERRUPT_TRANSFER *pBulkOrInterruptTransfer = (struct _URB_BULK_OR_INTERRUPT_TRANSFER *) pUrb;
//		SendToPipe (PC_BULK_DOWN, pBulkOrInterruptTransfer->TransferBufferLength, (char*)pBulkOrInterruptTransfer->TransferBuffer);
		if (pBulkOrInterruptTransfer->TransferBufferLength == 8)
		{
//			if (FindPN31Key((BYTE*)pBulkOrInterruptTransfer->TransferBuffer))
//				IoCancelIrp(Irp);
//			LogPrintf("CancelIRP  %d\n", ((char*)pBulkOrInterruptTransfer->TransferBuffer)[0]);
		}
	}

		// inspired from the macro code of IoSetCompletionRoutine
		// it just makes a big BSOD

		// ok. in fact, it worked, but that particular case,
		// the first parameter of the IoCompletionRoutine is NULL instead of being fido !!!

		// normally, there should be a call to IoCopyCurrentIrpStackLocationToNext()
		// which might not be there. So, we are surely replacing other callbacks.

		// first, we saved every information we'll modify later

		/*
			Windows 2000 DDK : Note that the context area cannot be pageable
			because the IoCompletion routine can be called at IRQL
			DISPATCH_LEVEL
		*/

		PCONTEXT Context = (PCONTEXT)ExAllocatePool(NonPagedPool,sizeof(CONTEXT));
		if (Context != NULL)
		{
			Context->CompletionRoutine = stack->CompletionRoutine;
			Context->Context           = stack->Context;
			Context->Control           = stack->Control;
			Context->pUrb              = pUrb;
			Context->uSequenceNumber   = uSequenceNumber;
			Context->Stack             = stack;

//			LogPrintf("  Replacing %p/%p/%p by ",
//				stack->CompletionRoutine,stack->Context,stack->Control));
			stack->CompletionRoutine = MyInternalIOCTLCompletion;
			stack->Context = Context;
			stack->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;

//			LogPrintf("%p/%p/%p\n",stack->CompletionRoutine,stack->Context,stack->Control));
		}
		else
			LogPrintf("  ExAllocatePool failed! Can't redirect CompletionRoutine\n");

		LogBuffer(&b);
		BufferDone(&b);
	}

	return pdx->OriginalDriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL](fdo,Irp);
}

/*
	DispatchPower - need a special code, since we have to call PoCallDriver()
	instead of IoCallDriver()
*/

NTSTATUS DispatchPower(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG fcn = stack->MinorFunction;
	
	const char* fcnname[] =
	{
		"IRP_MN_WAIT_WAKE",
		"IRP_MN_POWER_SEQUENCE",
		"IRP_MN_SET_POWER",
		"IRP_MN_QUERY_POWER",
	};
	
	if (fcn == IRP_MN_SET_POWER || fcn == IRP_MN_QUERY_POWER)
	{
		const char* sysstate[] =
		{
			"PowerSystemUnspecified",
			"PowerSystemWorking",
			"PowerSystemSleeping1",
			"PowerSystemSleeping2",
			"PowerSystemSleeping3",
			"PowerSystemHibernate",
			"PowerSystemShutdown",
			"PowerSystemMaximum",
		};
		
		const char* devstate[] =
		{
			"PowerDeviceUnspecified",
			"PowerDeviceD0",
			"PowerDeviceD1",
			"PowerDeviceD2",
			"PowerDeviceD3",
			"PowerDeviceMaximum",
		};
		
		ULONG context = stack->Parameters.Power.SystemContext;
		POWER_STATE_TYPE type = stack->Parameters.Power.Type;
		
		LogPrintf("PN31Snoop - IRP_MJ_POWER (%s), SystemContext %x", fcnname[fcn], context);
		if (type == SystemPowerState)
			LogPrintf(", SystemPowerState = %s\n", sysstate[stack->Parameters.Power.State.SystemState]);
		else
			LogPrintf(", DevicePowerState = %s\n", devstate[stack->Parameters.Power.State.DeviceState]);
	}
	else
		LogPrintf("PN31Snoop - IRP_MJ_POWER (%s)\n", fcnname[fcn]);
		
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
	PoStartNextPowerIrp(Irp);	// must be done while we own the IRP
	NTSTATUS status;
	status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
	if (!NT_SUCCESS(status))
		return CompleteRequest(Irp, status, 0);
	IoSkipCurrentIrpStackLocation(Irp);
	status = PoCallDriver(pdx->LowerDeviceObject, Irp);
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	return status;
}

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

NTSTATUS DispatchPnp(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG fcn = stack->MinorFunction;
	
/*	const char * MinorFunctionName = GetIrpPnpMinorFunctionName(fcn);
	if (MinorFunctionName != NULL)
		LogPrintf("PN31Snoop - DispatchPNP(%p) : IRP_MJ_PNP (%s)\n",
			DispatchPnp,MinorFunctionName);
	else
		LogPrintf("PN31Snoop - DispatchPNP(%p) : IRP_MJ_PNP (0x%x)\n",
			DispatchPnp,fcn);*/
	
	NTSTATUS status;
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
	status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
	if (!NT_SUCCESS(status))
		return CompleteRequest(Irp, status, 0);
	IoSkipCurrentIrpStackLocation(Irp);
	status = IoCallDriver(pdx->LowerDeviceObject, Irp);
	
	if (fcn == IRP_MN_REMOVE_DEVICE)
	{
		IoReleaseRemoveLockAndWait(&pdx->RemoveLock, Irp);
		RemoveDevice(fido);
	}
	else
		IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	
	return status;
}

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

NTSTATUS DispatchWmi(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{							// DispatchWmi
#if DBG
	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	ULONG fcn = stack->MinorFunction;
	
	static char* fcnname[] = {
		"IRP_MN_QUERY_ALL_DATA",
		"IRP_MN_QUERY_SINGLE_INSTANCE",
		"IRP_MN_CHANGE_SINGLE_INSTANCE",
		"IRP_MN_CHANGE_SINGLE_ITEM",
		"IRP_MN_ENABLE_EVENTS",
		"IRP_MN_DISABLE_EVENTS",
		"IRP_MN_ENABLE_COLLECTION",
		"IRP_MN_DISABLE_COLLECTION",
		"IRP_MN_REGINFO",
		"IRP_MN_EXECUTE_METHOD",
	};
	
	LogPrintf("PN31Snoop - IRP_MJ_SYSTEM_CONTROL (%s)\n", fcnname[fcn]);
#endif // DBG
	
	NTSTATUS status;
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fido->DeviceExtension;
	status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
	if (!NT_SUCCESS(status))
		return CompleteRequest(Irp, status, 0);
	IoSkipCurrentIrpStackLocation(Irp);
	status = IoCallDriver(pdx->LowerDeviceObject, Irp);
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	return status;
}

NTSTATUS DispatchCreateClose(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
    UNREFERENCED_PARAMETER(fido);

    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

    switch(stack->MajorFunction)
    {
    case IRP_MJ_CREATE:
        KdPrint(("USBSnpys::CreateClose() - IRP_MJ_CREATE\n"));
        break;
    case IRP_MJ_CLOSE:
        KdPrint(("USBSnpys::CreateClose() - IRP_MJ_CLOSE\n"));
        break;
    default:
        break;
    }

    return CompleteRequest(Irp, STATUS_SUCCESS, 0);
}

NTSTATUS DispatchInternalIOCTL(IN PDEVICE_OBJECT fido, IN PIRP Irp)
{
    UNREFERENCED_PARAMETER(fido);

    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    ULONG dwControlCode = stack->Parameters.DeviceIoControl.IoControlCode;
    PVOID inBuffer = Irp->AssociatedIrp.SystemBuffer;
    PVOID outBuffer = Irp->UserBuffer;
    ULONG inputBufferLength  = stack->Parameters.DeviceIoControl.InputBufferLength;
    ULONG outputBufferLength = stack->Parameters.DeviceIoControl.OutputBufferLength;

    NTSTATUS status = STATUS_INVALID_PARAMETER;
    switch(dwControlCode)
    {
    default:
        KdPrint(("USBSnpys - warning: unknown internal IOCTL: 0x%08x\n", dwControlCode));
        break;
    }

    return CompleteRequest(Irp, status, 0);
}

