
/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    Pitcher.c

Abstract:

    This module shows how to a write a generic filter driver.

Environment:

    Kernel mode

Revision History:

    Eliyas Yakub Oct 29 1998

    Fixed bugs - March 15, 2001

    Added Ioctl interface - Aug 16, 2001
    
    Updated to use IoCreateDeviceSecure function - Sep 17, 2002

    Updated to use RemLocks - Oct 29, 2002
    
--*/

#define MODULENAME "Pitcher.c"

#include "..\drivers.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, FilterAddDevice)
#pragma alloc_text (PAGE, FilterDispatchPnp)
#pragma alloc_text (PAGE, FilterUnload)
#endif

//
// Define IOCTL_INTERFACE in your sources file if  you want your
// app to have private interaction with the filter driver. Read KB Q262305
// for more information.
//


#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, FilterCreateControlObject)
#pragma alloc_text (PAGE, FilterDeleteControlObject)
#pragma alloc_text (PAGE, FilterDispatchIo)
#endif
FAST_MUTEX ControlMutex;
ULONG InstanceCount = 0;
PDEVICE_OBJECT ControlDeviceObject;

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 = STATUS_SUCCESS;
    ULONG               ulIndex;
    PDRIVER_DISPATCH  * dispatch;

    UNREFERENCED_PARAMETER (RegistryPath);

    DebugPrint (("loading\n"));


    //
    // Create dispatch points
    //
    for (ulIndex = 0, dispatch = DriverObject->MajorFunction;
         ulIndex <= IRP_MJ_MAXIMUM_FUNCTION;
         ulIndex++, dispatch++) {

        *dispatch = FilterPass;
    }
    DriverObject->DriverExtension->AddDevice           = FilterAddDevice;
    DriverObject->DriverUnload                         = FilterUnload;
    return status;
}


NTSTATUS
FilterAddDevice(
    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;
    ULONG                   deviceType = FILE_DEVICE_UNKNOWN;

    PAGED_CODE ();


    //
    // IoIsWdmVersionAvailable(1, 0x20) returns TRUE on os after Windows 2000.
    //
    if (!IoIsWdmVersionAvailable(1, 0x20)) {
        //
        // Win2K system bugchecks if the filter attached to a storage device
        // doesn't specify the same DeviceType as the device it's attaching
        // to. This bugcheck happens in the filesystem when you disable
        // the devicestack whose top level deviceobject doesn't have a VPB.
        // To workaround we will get the toplevel object's DeviceType and
        // specify that in IoCreateDevice.
        //
        deviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
        deviceType = deviceObject->DeviceType;
        ObDereferenceObject(deviceObject);
    }

    //
    // Create a filter device object.
    //

    status = IoCreateDevice (DriverObject,
                             sizeof (DEVICE_EXTENSION),
                             NULL,  // No Name
                             deviceType,
                             FILE_DEVICE_SECURE_OPEN,
                             FALSE,
                             &deviceObject);


    if (!NT_SUCCESS (status)) {
        //
        // Returning failure here prevents the entire stack from functioning,
        // but most likely the rest of the stack will not be able to create
        // device objects either, so it is still OK.
        //
        return status;
    }

    DebugPrint (("AddDevice PDO (0x%x) FDO (0x%x)\n",
                    PhysicalDeviceObject, deviceObject));

    deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;

    deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack (
                                       deviceObject,
                                       PhysicalDeviceObject);
    //
    // Failure for attachment is an indication of a broken plug & play system.
    //

    if(NULL == deviceExtension->NextLowerDriver) {

        IoDeleteDevice(deviceObject);
        return STATUS_UNSUCCESSFUL;
    }

    deviceObject->Flags |= deviceExtension->NextLowerDriver->Flags &
                            (DO_BUFFERED_IO | DO_DIRECT_IO |
                            DO_POWER_PAGABLE );


    deviceObject->DeviceType = deviceExtension->NextLowerDriver->DeviceType;

    deviceObject->Characteristics =
                          deviceExtension->NextLowerDriver->Characteristics;

    deviceExtension->Self = deviceObject;

    //
    // Let us use remove lock to keep count of IRPs so that we don't 
    // deteach and delete our deviceobject until all pending I/Os in our
    // devstack are completed. Remlock is required to protect us from
    // various race conditions where our driver can get unloaded while we
    // are still running dispatch or completion code.
    //
    
    IoInitializeRemoveLock (&deviceExtension->RemoveLock , 
                            POOL_TAG,
                            1, // MaxLockedMinutes 
                            100); // HighWatermark, this parameter is 
                                // used only on checked build. Specifies 
                                // the maximum number of outstanding 
                                // acquisitions allowed on the lock
                                

    //
    // Set the initial state of the Filter DO
    //

    INITIALIZE_PNP_STATE(deviceExtension);

    DebugPrint(("AddDevice: %x to %x->%x \n", deviceObject,
                       deviceExtension->NextLowerDriver,
                       PhysicalDeviceObject));

    deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    return STATUS_SUCCESS;

}


NTSTATUS
FilterPass (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

Routine Description:

    The driver dispatch routine.  If this IRP is a Software Write Blocker test
    IRP we complete it here with status STATUS_SWBTS_CAUGHT. This status indicates
    the IRP was passed by upper level drivers. Otherwise we send it on for normal
    processing.

    As we have NO idea which function we are happily passing on, we can make
    NO assumptions about whether or not it will be called at raised IRQL.
    For this reason, this function must be in put into non-paged pool
    (aka the default location).

Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

      NT status code

--*/
{
    PDEVICE_EXTENSION           deviceExtension;
    PIO_STACK_LOCATION		pIoStack;
    NTSTATUS                    status;
   
    // Acquire a remove lock for ourself
 
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    status = IoAcquireRemoveLock (&deviceExtension->RemoveLock, Irp);
    if (!NT_SUCCESS (status))
    {
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        return status;
    }

    // If the IRP is from the test suite just complete it
    // after marking with a caught status

    if(Irp->IoStatus.Information == IOCTL_IRP_PASSTHRU)
    {
       DebugPrint(("Caught Passthru IRP\n"));
       Irp->IoStatus.Status = STATUS_SWBTS_CAUGHT;
       Irp->IoStatus.Information = 0;
       IoCompleteRequest(Irp, IO_NO_INCREMENT);
       IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp);
       return STATUS_SUCCESS;
    }

    // Not a test IRP so dispatch it normally

    pIoStack = IoGetCurrentIrpStackLocation(Irp);
    switch(pIoStack->MajorFunction)
    {
       case IRP_MJ_PNP:
	  return FilterDispatchPnp(DeviceObject, Irp);
    
       case IRP_MJ_POWER:
          deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
          PoStartNextPowerIrp(Irp);
          IoSkipCurrentIrpStackLocation(Irp);
          status = PoCallDriver(deviceExtension->NextLowerDriver, Irp);
          IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
          return status;

       default:
          deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
          IoSkipCurrentIrpStackLocation (Irp);
          status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
          IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
          return status;
    }
}

NTSTATUS
FilterDispatchPnp (
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

Routine Description:

    The plug and play dispatch routines.

    Most of these the driver will completely ignore.
    In all cases it must pass on the IRP to the lower driver.

Arguments:

   DeviceObject - pointer to a device object.

   Irp - pointer to an I/O Request Packet.

Return Value:

      NT status code

--*/
{
    PDEVICE_EXTENSION           deviceExtension;
    PIO_STACK_LOCATION         irpStack;
    NTSTATUS                            status;
    KEVENT                               event;

    PAGED_CODE();

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
    irpStack = IoGetCurrentIrpStackLocation(Irp);

    DebugPrint(("%s\n", PnPMinorFunctionString(irpStack->MinorFunction)));

    switch (irpStack->MinorFunction) {
    case IRP_MN_START_DEVICE:
		DebugPrint(("Passing START_DEVICE to lower drivers\n"));

        //
        // The device is starting.
        // We cannot touch the device (send it any non pnp irps) until a
        // start device has been passed down to the lower drivers.
        //
        KeInitializeEvent(&event, NotificationEvent, FALSE);
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutine(Irp,
                               (PIO_COMPLETION_ROUTINE) FilterStartCompletionRoutine,
                               &event,
                               TRUE,
                               TRUE,
                               TRUE);

        status = IoCallDriver(deviceExtension->NextLowerDriver, Irp);
        
        //
        // Wait for lower drivers to be done with the Irp. Important thing to
        // note here is when you allocate memory for an event in the stack  
        // you must do a KernelMode wait instead of UserMode to prevent 
        // the stack from getting paged out.
        //
		DebugPrint(("Waiting for lower drivers\n"));
        if (status == STATUS_PENDING) {

           KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);          
           status = Irp->IoStatus.Status;
        }
		DebugPrint(("START_DEVICE completion status: 0x%x\n"));
        if (NT_SUCCESS (status)) {

            //
            // As we are successfully now back, we will
            // first set our state to Started.
            //

            SET_NEW_PNP_STATE(deviceExtension, Started);

            //
            // On the way up inherit FILE_REMOVABLE_MEDIA during Start.
            // This characteristic is available only after the driver stack is started!.
            //
            if (deviceExtension->NextLowerDriver->Characteristics & FILE_REMOVABLE_MEDIA) {

                DeviceObject->Characteristics |= FILE_REMOVABLE_MEDIA;
            }
        }
        
        Irp->IoStatus.Status = status;
        IoCompleteRequest (Irp, IO_NO_INCREMENT);
        IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
        return status;

    case IRP_MN_REMOVE_DEVICE:

        //
        // Wait for all outstanding requests to complete
        //
        DebugPrint(("Waiting for outstanding requests\n"));
        IoReleaseRemoveLockAndWait(&deviceExtension->RemoveLock, Irp);

        IoSkipCurrentIrpStackLocation(Irp);

        status = IoCallDriver(deviceExtension->NextLowerDriver, Irp);

        SET_NEW_PNP_STATE(deviceExtension, Deleted);
        
        IoDetachDevice(deviceExtension->NextLowerDriver);
        IoDeleteDevice(DeviceObject);
        return status;


    case IRP_MN_QUERY_STOP_DEVICE:
        SET_NEW_PNP_STATE(deviceExtension, StopPending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_STOP_DEVICE:

        //
        // Check to see whether you have received cancel-stop
        // without first receiving a query-stop. This could happen if someone
        // above us fails a query-stop and passes down the subsequent
        // cancel-stop.
        //

        if(StopPending == deviceExtension->DevicePnPState)
        {
            //
            // We did receive a query-stop, so restore.
            //
            RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
        }
        status = STATUS_SUCCESS; // We must not fail this IRP.
        break;

    case IRP_MN_STOP_DEVICE:
        SET_NEW_PNP_STATE(deviceExtension, Stopped);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_QUERY_REMOVE_DEVICE:

        SET_NEW_PNP_STATE(deviceExtension, RemovePending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_SURPRISE_REMOVAL:

        SET_NEW_PNP_STATE(deviceExtension, SurpriseRemovePending);
        status = STATUS_SUCCESS;
        break;

    case IRP_MN_CANCEL_REMOVE_DEVICE:

        //
        // Check to see whether you have received cancel-remove
        // without first receiving a query-remove. This could happen if
        // someone above us fails a query-remove and passes down the
        // subsequent cancel-remove.
        //

        if(RemovePending == deviceExtension->DevicePnPState)
        {
            //
            // We did receive a query-remove, so restore.
            //
            RESTORE_PREVIOUS_PNP_STATE(deviceExtension);
        }

        status = STATUS_SUCCESS; // We must not fail this IRP.
        break;

    case IRP_MN_DEVICE_USAGE_NOTIFICATION:

        //
        // On the way down, pagable might become set. Mimic the driver
        // above us. If no one is above us, just set pagable.
        //
        if ((DeviceObject->AttachedDevice == NULL) ||
            (DeviceObject->AttachedDevice->Flags & DO_POWER_PAGABLE)) {

            DeviceObject->Flags |= DO_POWER_PAGABLE;
        }

        IoCopyCurrentIrpStackLocationToNext(Irp);

        IoSetCompletionRoutine(
            Irp,
            FilterDeviceUsageNotificationCompletionRoutine,
            NULL,
            TRUE,
            TRUE,
            TRUE
            );

        return IoCallDriver(deviceExtension->NextLowerDriver, Irp);

    default:
        //
        // If you don't handle any IRP you must leave the
        // status as is.
        //
        status = Irp->IoStatus.Status;

        break;
    }

    //
    // Pass the IRP down and forget it.
    //
    Irp->IoStatus.Status = status;
    IoSkipCurrentIrpStackLocation (Irp);
    status = IoCallDriver (deviceExtension->NextLowerDriver, Irp);
    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 
    return status;
}


NTSTATUS
FilterStartCompletionRoutine(IN PDEVICE_OBJECT   DeviceObject,
    			     IN PIRP             PassthruIrp,
    			     IN PVOID            Context )


/*++
Routine Description:
    A completion routine for use when calling the lower device objects to
    which our filter deviceobject is attached.

Arguments:

    DeviceObject - Pointer to deviceobject
    Irp          - Pointer to a PnP Irp.
    Context      - NULL
Return Value:

    NT Status is returned.

--*/

{
    PKEVENT             event = (PKEVENT)Context;

    UNREFERENCED_PARAMETER (DeviceObject);

    //
    // If the lower driver didn't return STATUS_PENDING, we don't need to 
    // set the event because we won't be waiting on it. 
    // This optimization avoids grabbing the dispatcher lock, and improves perf.
    //
    if (PassthruIrp->PendingReturned == TRUE) {
        KeSetEvent (event, IO_NO_INCREMENT, FALSE);
    }

    //
    // The dispatch routine will have to call IoCompleteRequest
    //

    return STATUS_MORE_PROCESSING_REQUIRED;

}

NTSTATUS
FilterDeviceUsageNotificationCompletionRoutine(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
/*++
Routine Description:
    A completion routine for use when calling the lower device objects to
    which our filter deviceobject is attached.

Arguments:

    DeviceObject - Pointer to deviceobject
    Irp          - Pointer to a PnP Irp.
    Context      - NULL
Return Value:

    NT Status is returned.

--*/

{
    PDEVICE_EXTENSION       deviceExtension;

    UNREFERENCED_PARAMETER(Context);

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;


    if (Irp->PendingReturned) {

        IoMarkIrpPending(Irp);
    }

    //
    // On the way up, pagable might become clear. Mimic the driver below us.
    //
    if (!(deviceExtension->NextLowerDriver->Flags & DO_POWER_PAGABLE)) {

        DeviceObject->Flags &= ~DO_POWER_PAGABLE;
    }

    IoReleaseRemoveLock(&deviceExtension->RemoveLock, Irp); 

    return STATUS_CONTINUE_COMPLETION;
}

VOID
FilterUnload(
    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.
    //
    ASSERT(DriverObject->DeviceObject == NULL);

    //
    // We should not be unloaded until all the devices we control
    // have been removed from our queue.
    //
    DebugPrint (("unload\n"));

    return;
}


NTSTATUS
FilterDispatchIo(
    IN PDEVICE_OBJECT    DeviceObject,
    IN PIRP              Irp
    )
/*++

Routine Description:

    This routine is the dispatch routine for non-pnp IRPs.

    If the request is for our control device we will process and complete the
    control request in this routine.

    If the request is not a an IOCTL_IRP_PASSTHRU request we will simply pass
    the request down the stack.

    If the request is an IOCTL_IRP_PASSTHRU request we will initiate a new IRP
    using the IRP information contained in the IOCTL_IRP_PASSTHRU input buffer
    and leave the original IRP pending until the new IRP completes:

    1) We allocate and initialize the new IRP.

    2) We flag the new IRP as a passthru request by setting the information field
       in the IOSB for the new IRP to IOCTL_IRP_PASSTHRU. This will allow our lower
       filter driver to catch and complete the IRP if the intervening drivers do not
       block it.

    4) We attach our completion routine to the new IRP and pass the new IRP to
       the next lower driver on the stack. We pass the pointer to the original
       IRP as the context pointer for the completion routine.

    5) We call the next lower driver on the stack with the new IRP and return
       STATUS_MORE_PROCESSING_REQUIRED to the I/O Manager.

   
Arguments:

    DeviceObject - Pointer to the device object.

    Irp - Pointer to the request packet.

Return Value:

    NT Status code
--*/
{
    PCONTROL_DEVICE_EXTENSION	pCtlDeviceExtension;
    PDEVICE_EXTENSION			pDeviceExtension;
    PIO_STACK_LOCATION			irpStack, pMySlot, pNextSlot;
    PDEVICE_OBJECT				pNextDevice;
    NTSTATUS					status;
	PUSER_IRP					pUsrIrp;
    PIRP						pKerIrp;  

    PAGED_CODE();

    pMySlot = IoGetCurrentIrpStackLocation (Irp);



     // If this is not an IOCTL_IRP_PASSTHRU request just pass it on

     if((pMySlot->MajorFunction != IRP_MJ_DEVICE_CONTROL) ||
        (pMySlot->Parameters.DeviceIoControl.IoControlCode != IOCTL_IRP_PASSTHRU))
     { 
       return FilterPass(DeviceObject, Irp);    
     }

     // We've been asked to initiate an IRP - validate the caller's buffer

     DebugPrint(("IOCTL_IRP_PASSTHRU IRP  @0x%8.8x\n", Irp));
     DebugPrint(("I/O Stack Location      @0x%8.8x\n", pMySlot));
     DebugPrint(("System buffer           @0x%8.8x\n", Irp->AssociatedIrp.SystemBuffer));

     pUsrIrp = (PUSER_IRP) Irp->AssociatedIrp.SystemBuffer;
     if(!pUsrIrp ||
		pMySlot->Parameters.DeviceIoControl.InputBufferLength < sizeof(USER_IRP) ||
		pMySlot->Parameters.DeviceIoControl.OutputBufferLength < sizeof(USER_IRP))
	 {
		DebugPrint(("Invalid user buffer, Address: 0x%x, Input Length: %d, Output Length: %d\n",
					pUsrIrp,
					pMySlot->Parameters.DeviceIoControl.InputBufferLength,
					pMySlot->Parameters.DeviceIoControl.OutputBufferLength));
		Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
		IoCompleteRequest(Irp, IO_NO_INCREMENT);
		return STATUS_INVALID_PARAMETER;
	 } 

     DebugPrint(("User I/O Status block: 0x%8.8x, 0x%8.8x\n",
                                         pUsrIrp->IoStatus.Status,
                                         pUsrIrp->IoStatus.Information));
	// Allocate a new kernel IRP with enough stack slots for this driver and
	// all lower drivers (we need a slot for our completion routine).
    // Setup our completion routine for the kernel IRP. We pass the original
	// IOCTL IRP to the completion routine as the context pointer so it can be
	// completed as well.

	pKerIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);									// Allocate a new IRP for that device
        IoSetNextIrpStackLocation(pKerIrp);													// Push stack pointer to first slot
	IoSetCompletionRoutine(pKerIrp,														// Target IRP
						   SwbtsIrpPassthruCompletion,									// Entry point of completion routine,
						   Irp,															// Pass original IRP as context to completion routine
						   TRUE, TRUE, TRUE);											// Handle SUCCESS, ERROR, and CANCEL events

	// Copy the user's IRP data to the next lower driver's stack location

	pNextSlot = IoGetNextIrpStackLocation(pKerIrp);										// Get next drivers slot
	pNextSlot->MajorFunction = pUsrIrp->IoStack.MajorFunction;							// Major function code
	pNextSlot->MinorFunction = pUsrIrp->IoStack.MinorFunction;							// Minor function code
	pNextSlot->Parameters = pUsrIrp->IoStack.Parameters;								// Function specific parameters
	pNextSlot->FileObject = pUsrIrp->IoStack.FileObject;								// Haven't a clue...

	// Initialize the I/O status block for the IRP. Set a flag in the
	// Information member to flag this IRP as a test suite IRP on the
	// way down so our lower filter can catch and complete it if it gets
	// that far. This assumes lower drivers will not modify the information
	// field unless they are completing the request.

	pKerIrp->IoStatus.Status = 0;
	pKerIrp->IoStatus.Information = IOCTL_IRP_PASSTHRU;

	// Fire in the hole - start the request - God help us...

	pDeviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;				// Device extension for our device
	pNextDevice = pDeviceExtension->NextLowerDriver;									// Saved pointer to next device on stack 
	IoMarkIrpPending(Irp);
        DebugPrint(("Starting IRP @0x%8.8x\n", pKerIrp));																// Mark the original IRP pending
	status= IoCallDriver(pNextDevice, pKerIrp);											// Start the replacement IRP
	return STATUS_PENDING;																// Return status to I/O Manager
}

NTSTATUS
SwbtsIrpPassthruCompletion(IN PDEVICE_OBJECT   DeviceObject,
    			   IN PIRP             pKerIrp,
    			   IN PVOID            Context )


/*++
Routine Description:

    This routine is called to complete processing of a (kernel) IRP generated by an IOCTL_IRP_PASSTHRU
    request to this driver. The IOCTL request passed a user mode copy of an IRP in the input buffer of
    the request. Our dispatch routine started  a kernel IRP using a copy of the the data in the user's
    buffer and set this routine as the completion routine for it.  Our dispatch routine has passed us
    a pointer to the original IRP in the context argument to this call.  Now we copy the I/O status block
    of the generated IRP back into the I/O status block of the IRP in the user's buffer and complete both
    IRPs.

Arguments:

    DeviceObject - Pointer to deviceobject
    pKerIrp      - Pointer to a driver allocated Irp.
    Context      - Pointer to the IOCTL_IRP_PASSTHRU IRP that generated the IRP that
                   we have been called to complete

Return Value:

    NT Status is returned.

--*/

{
    PIRP                   pIoctlIrp; 
    PUSER_IRP              pUsrIrp;
    UNREFERENCED_PARAMETER (DeviceObject);
    DebugPrint(("Completing IRP @0x%8.8x\n", pKerIrp));
    DebugPrint(("Context IRP @0x%8.8x\n", Context));
    DebugPrint(("Completion status: 0x%8.8x, 0x%8.8x\n", pKerIrp->IoStatus.Status, pKerIrp->IoStatus.Information));
    // Copy the status of the driver allocated IRP to the I/O status block of the user mode
    // copy of the IRP passed in the input/output buffer of the original IOCTL IRP

    pIoctlIrp = (PIRP) Context;
    DebugPrint(("IOCTL Output buffer: 0x%8.8x\n", pIoctlIrp->AssociatedIrp.SystemBuffer));
    pUsrIrp = (PUSER_IRP) pIoctlIrp->AssociatedIrp.SystemBuffer;
    RtlCopyBytes(&(pUsrIrp->IoStatus), &(pKerIrp->IoStatus), sizeof(pUsrIrp->IoStatus));


    // Complete the IOCTL IRP

    pIoctlIrp->IoStatus.Status = STATUS_SUCCESS;
    pIoctlIrp->IoStatus.Information = sizeof(USER_IRP);
    IoCompleteRequest(pIoctlIrp, IO_NO_INCREMENT);
    
    // Free the driver allocated IRP and tell the I/O manager no additional
    // processing on his part is needed.

    IoFreeIrp(pKerIrp);
    return STATUS_MORE_PROCESSING_REQUIRED;
}

#if DBG

PCHAR
PnPMinorFunctionString (
    UCHAR MinorFunction
)
{
    switch (MinorFunction)
    {
        case IRP_MN_START_DEVICE:
            return "IRP_MN_START_DEVICE";
        case IRP_MN_QUERY_REMOVE_DEVICE:
            return "IRP_MN_QUERY_REMOVE_DEVICE";
        case IRP_MN_REMOVE_DEVICE:
            return "IRP_MN_REMOVE_DEVICE";
        case IRP_MN_CANCEL_REMOVE_DEVICE:
            return "IRP_MN_CANCEL_REMOVE_DEVICE";
        case IRP_MN_STOP_DEVICE:
            return "IRP_MN_STOP_DEVICE";
        case IRP_MN_QUERY_STOP_DEVICE:
            return "IRP_MN_QUERY_STOP_DEVICE";
        case IRP_MN_CANCEL_STOP_DEVICE:
            return "IRP_MN_CANCEL_STOP_DEVICE";
        case IRP_MN_QUERY_DEVICE_RELATIONS:
            return "IRP_MN_QUERY_DEVICE_RELATIONS";
        case IRP_MN_QUERY_INTERFACE:
            return "IRP_MN_QUERY_INTERFACE";
        case IRP_MN_QUERY_CAPABILITIES:
            return "IRP_MN_QUERY_CAPABILITIES";
        case IRP_MN_QUERY_RESOURCES:
            return "IRP_MN_QUERY_RESOURCES";
        case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
            return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
		case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:
			return "IRP_MN_QUERY_LEGACY_BUS_INFORMATION";
        case IRP_MN_QUERY_DEVICE_TEXT:
            return "IRP_MN_QUERY_DEVICE_TEXT";
        case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
            return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
        case IRP_MN_READ_CONFIG:
            return "IRP_MN_READ_CONFIG";
        case IRP_MN_WRITE_CONFIG:
            return "IRP_MN_WRITE_CONFIG";
        case IRP_MN_EJECT:
            return "IRP_MN_EJECT";
        case IRP_MN_SET_LOCK:
            return "IRP_MN_SET_LOCK";
        case IRP_MN_QUERY_ID:
            return "IRP_MN_QUERY_ID";
        case IRP_MN_QUERY_PNP_DEVICE_STATE:
            return "IRP_MN_QUERY_PNP_DEVICE_STATE";
        case IRP_MN_QUERY_BUS_INFORMATION:
            return "IRP_MN_QUERY_BUS_INFORMATION";
        case IRP_MN_DEVICE_USAGE_NOTIFICATION:
            return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
        case IRP_MN_SURPRISE_REMOVAL:
            return "IRP_MN_SURPRISE_REMOVAL";

        default:
            return "unknown_pnp_irp";
    }
}

#endif

