//{ **************************************************************************** }
//{ FileName............: PLUGPLAY.CPP                                           }
//{ Project.............: FLEXCOP                                                }
//{ Author(s)...........: M.Majoor                                               }
//{ Original contents...: Walter Oney ('Programming the Windows Driver Model')   }
//{ Version.............: 2.00                                                   }
//{ ---------------------------------------------------------------------------- }
//{ Plug and Play handlers for FLEXCOP driver.                                   }
//{                                                                              }
//{ Version  Date      Comment                                                   }
//{ 1.00     20050410  - Initial release based on SAA7146A driver V2.02          }
//{ 2.00     20060722  - No changes                                              }
//{ **************************************************************************** }


#include "stddcls.h"
#include "driver.h"


//{ **************************************************************************** }
//{ Descript : Forwards.                                                         }
//{ **************************************************************************** }
NTSTATUS DefaultPnpHandler      (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleCancelRemove     (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleCancelStop       (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleQueryCapabilities(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleQueryRemove      (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleQueryStop        (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleRemoveDevice     (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleStartDevice      (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleStopDevice       (IN PDEVICE_OBJECT fdo, IN PIRP Irp);
NTSTATUS HandleSurpriseRemoval  (IN PDEVICE_OBJECT fdo, IN PIRP Irp);

#if DBG
  VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list);

  static char* statenames[] =
  {
    "STOPPED",
    "WORKING",
    "PENDINGSTOP",
    "PENDINGREMOVE",
    "SURPRISEREMOVED",
    "REMOVED",
  };
#endif


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Dispatcher of PNP requests.                                       }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS DispatchPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  PAGED_CODE();
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
  if (!NT_SUCCESS(status))
    return CompleteRequest(Irp, status);

  // All the parameters for the IRP, including the all-important minor function code,
  // are in the stack location. Hence, we obtain a pointer to it by calling
  // IoGetCurrentIrpStackLocation.
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  ASSERT(stack->MajorFunction == IRP_MJ_PNP);

  // A method of handling the two dozen possible minor function codes is to write
  // a subdispatch function for each one we're going to handle and then to define
  // a table of pointers to those subdispatch functions. Many of the entries in the
  // table will be DefaultPnpHandler. Subdispatch functions like HandleStartDevice
  // will take pointers to a device object and an IRP as parameters and will return
  // an NTSTATUS code.
  static NTSTATUS (*fcntab[])(IN PDEVICE_OBJECT fdo, IN PIRP Irp) =
  {
    HandleStartDevice,                                     // IRP_MN_START_DEVICE
    HandleQueryRemove,                                     // IRP_MN_QUERY_REMOVE_DEVICE
    HandleRemoveDevice,                                    // IRP_MN_REMOVE_DEVICE
    HandleCancelRemove,                                    // IRP_MN_CANCEL_REMOVE_DEVICE
    HandleStopDevice,                                      // IRP_MN_STOP_DEVICE
    HandleQueryStop,                                       // IRP_MN_QUERY_STOP_DEVICE
    HandleCancelStop,                                      // IRP_MN_CANCEL_STOP_DEVICE
    DefaultPnpHandler,                                     // IRP_MN_QUERY_DEVICE_RELATIONS
    DefaultPnpHandler,                                     // IRP_MN_QUERY_INTERFACE
    HandleQueryCapabilities,                               // IRP_MN_QUERY_CAPABILITIES
    DefaultPnpHandler,                                     // IRP_MN_QUERY_RESOURCES
    DefaultPnpHandler,                                     // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
    DefaultPnpHandler,                                     // IRP_MN_QUERY_DEVICE_TEXT
    DefaultPnpHandler,                                     // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
    DefaultPnpHandler,                                     //
    DefaultPnpHandler,                                     // IRP_MN_READ_CONFIG
    DefaultPnpHandler,                                     // IRP_MN_WRITE_CONFIG
    DefaultPnpHandler,                                     // IRP_MN_EJECT
    DefaultPnpHandler,                                     // IRP_MN_SET_LOCK
    DefaultPnpHandler,                                     // IRP_MN_QUERY_ID
    DefaultPnpHandler,                                     // IRP_MN_QUERY_PNP_DEVICE_STATE
    DefaultPnpHandler,                                     // IRP_MN_QUERY_BUS_INFORMATION
    DefaultPnpHandler,                                     // IRP_MN_DEVICE_USAGE_NOTIFICATION
    HandleSurpriseRemoval,                                 // IRP_MN_SURPRISE_REMOVAL
                                                           // Windows 98 never sends an IRP_MN_SURPRISE_REMOVAL request.
  };

  // We expect the IRP's minor function code to be one of those listed in the table
  ULONG fcn = stack->MinorFunction;
  // If we get a minor function code we don't recognize, it's probably because
  // Microsoft defined a new one in a release of the DDK after the DDK with which
  // we built our driver. The right thing to do is to pass the minor function code
  // down the stack by calling the default handler. By the way, arraysize is a macro
  // that returns the number of elements in an array. It's defined as
  //   #define arraysize(p) (sizeof(p)/sizeof((p)[0])).
  if (fcn >= arraysize(fcntab))
  {                                                        // If unknown function
    status = DefaultPnpHandler(fdo, Irp);                  // Some function we don't know about
    IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
    return status;
  }

  #if DBG
    static 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",
    };

    KdPrint((DRIVERNAME " - PNP Request (%s)\n", fcnname[fcn]));
  #endif

  // This is the operative statement in the dispatch routine, with which we index
  // the table of subdispatch functions and call the right one.
  status = (*fcntab[fcn])(fdo, Irp);
  if (fcn != IRP_MN_REMOVE_DEVICE)
    IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
  return status;
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Default PNP handler.                                              }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS DefaultPnpHandler(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  IoSkipCurrentIrpStackLocation(Irp);
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  return IoCallDriver(pdx->LowerDeviceObject, Irp);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP cancel remove request.                                 }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleCancelRemove(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  // We get the CANCEL_REMOVE request when something either above or below us vetoes
  // a QUERY_REMOVE. If we never saw the query, we'll still be in the WORKING state
  // and don't need to do anything with this IRP. Otherwise, we need to forward it
  // to the lower levels before we process it because we want the lower levels to be
  // ready to process the IRPs we're about to release from our queues.
  if (pdx->state == PENDINGREMOVE)
  {                                                        // If we succeeded earlier query
    // Lower-level drivers are presumably in the pending-remove state as
    // well, so we need to tell them that the remove has been cancelled
    // before we start sending IRPs down to them.
    NTSTATUS status = ForwardAndWait(fdo, Irp);            // Wait for lower layers
    if (NT_SUCCESS(status))
    {                                                      // If completed successfully
      // Here, we undo the steps we took when we succeeded the QUERY_REMOVE. We revert
      // to the previous state. If the previous state was WORKING, we stalled the queues
      // when we handled the query and need to unstall them now.
      KdPrint((DRIVERNAME " - To %s from PENDINGREMOVE\n", statenames[pdx->prevstate]));
      if ((pdx->state = pdx->prevstate) == WORKING)
      {                                                    // If back to working state
        // TODO If you use multiple queues, code this instead:
        //	RestartAllRequests(pdx->queues, arraysize(pdx->queues), fdo);
        RestartRequests(&pdx->dqReadWrite, fdo);
      }
    }
    else
      KdPrint((DRIVERNAME " - Status %8.8lX returned by PDO for IRP_MN_CANCEL_REMOVE_DEVICE", status));

    return CompleteRequest(Irp, status);
  }

  return DefaultPnpHandler(fdo, Irp);                      // Unexpected cancel
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP cancel stop request.                                   }
//{            We first check to see whether a stop operation is even pending.   }
//{            Some higher-level driver might have vetoed a query that we never  }
//{            saw, so we'd still be in the WORKING state. If we're not in the   }
//{            PENDINGSTOP state, we simply forward the IRP. Otherwise, we send  }
//{            the CANCEL_STOP IRP synchronously to the lower-level drivers. That}
//{            is, we use our ForwardAndWait helper function to send the IRP down}
//{            the stack and await its completion. We wait for low-level drivers }
//{            because we're about to resume processing IRPs, and the drivers    }
//{            might have work to do before we send them an IRP. If the lower    }
//{            layers successfully handle this IRP_MN_CANCEL_STOP_DEVICE, we     }
//{            change our state variable to indicate that we're back in the      }
//{            WORKING state, and we call RestartRequests to unstall the queues  }
//{            we stalled when we succeeded the query.                           }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleCancelStop(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  if (pdx->state == PENDINGSTOP)
  {                                                        // If we succeeded earlier query
    // Lower level drivers are presumably in the pending-stop state as
    // well, so we need to tell them that the stop has been cancelled
    // before we start sending IRPs down to them.
    NTSTATUS status = ForwardAndWait(fdo, Irp);            // Wait for lower layers
    if (NT_SUCCESS(status))
    {                                                      // If completed successfully
      KdPrint((DRIVERNAME " - To WORKING from PENDINGSTOP\n"));
      pdx->state = WORKING;

      // TODO If you use multiple queues, code this instead:
      //	RestartAllRequests(pdx->queues, arraysize(pdx->queues), fdo);
      RestartRequests(&pdx->dqReadWrite, fdo);
    }
    else
      KdPrint((DRIVERNAME " - Status %8.8lX returned by PDO for IRP_MN_CANCEL_STOP_DEVICE", status));

    return CompleteRequest(Irp, status);
  }

  return DefaultPnpHandler(fdo, Irp);                      // Unexpected cancel
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP query capabilities request.                            }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleQueryCapabilities(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_QUERY_CAPABILITIES);
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  PDEVICE_CAPABILITIES pdc = stack->Parameters.DeviceCapabilities.Capabilities;
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // The device capabilities structure has a version number member, which is
  // currently always equal to 1. The structure is designed to always be upward
  // compatible, so you'll be able to work with the version defined in the DDK
  // that you build your driver with and with any later incarnation of the structure.
  // If, however, you're confronted with a structure that's older than you're able
  // to work with, you should just ignore this IRP by passing it along.

  // Check to be sure we know how to handle this version of the capabilities structure
  if (pdc->Version < 1)
    return DefaultPnpHandler(fdo, Irp);

  NTSTATUS status = ForwardAndWait(fdo, Irp);
  if (NT_SUCCESS(status))
  {                                                        // If IRP succeeded
    stack = IoGetCurrentIrpStackLocation(Irp);
    pdc = stack->Parameters.DeviceCapabilities.Capabilities;

    // Here's where you can override any capabilities that were set by the bus driver
    // TODO Modify any capabilities that must be set on the way back up

    // It's a good idea to make a copy of the capabilities structure. You use the
    // DeviceState map (when you receive a system power IRP). You might have occasion
    // to consult other fields in the structure, too.
    pdx->devcaps = *pdc;                                   // Save capabilities for whoever needs to see them
  }

  return CompleteRequest(Irp, status);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP query remove request.                                  }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleQueryRemove(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_QUERY_REMOVE_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  if (pdx->state == WORKING)
  {                                                        // If currently working
    #ifdef _X86_
      // Win98 doesn't check for open handles before allowing a remove to proceed,
      // and it may deadlock in IoReleaseRemoveLockAndWait if handles are still
      // open.
      if (win98 && pdx->DeviceObject->ReferenceCount)
      {
        KdPrint((DRIVERNAME " - Failing removal query due to open handles\n"));
        return CompleteRequest(Irp, STATUS_DEVICE_BUSY);
      }
    #endif
    // TODO If you use multiple queues, code this instead:
    //   if (CheckAnyBusyAndStall(pdx->queues, arraysize(pdx->queues), fdo);

    // You want to stall the request queue and wait for a short period, if necessary,
    // until the current request finishes.
    if (CheckBusyAndStall(&pdx->dqReadWrite))
      return CompleteRequest(Irp, STATUS_UNSUCCESSFUL);
    KdPrint((DRIVERNAME " - To PENDINGREMOVE from %s\n", statenames[pdx->state]));
  }

  // Save current state for restoration if the query gets cancelled.
  // (We can now be stopped or working)
  pdx->prevstate = pdx->state;
  pdx->state = PENDINGREMOVE;
  return DefaultPnpHandler(fdo, Irp);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP query stop request.                                    }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleQueryStop(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_QUERY_STOP_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // This statement handles a peculiar situation that can arise for a boot device:
  // the PnP Manager might send you a QUERY_STOP when you haven't initialized yet.
  // You want to ignore such a query, which is tantamount to saying "yes."

  // Boot devices may get this query before they even start, so check to see
  // if we're in the WORKING state before doing anything.
  if (pdx->state != WORKING)
    return DefaultPnpHandler(fdo, Irp);

  // TODO If you use multiple queues, code this instead:
  //	if (CheckAnyBusyAndStall(pdx->queues, arraysize(pdx->queues), fdo);
  if (CheckBusyAndStall(&pdx->dqReadWrite))
    return CompleteRequest(Irp, STATUS_UNSUCCESSFUL);
  KdPrint((DRIVERNAME " - To PENDINGSTOP from %s\n", statenames[pdx->state]));
  pdx->state = PENDINGSTOP;
  return DefaultPnpHandler(fdo, Irp);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP remove device request.                                 }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleRemoveDevice(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_REMOVE_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // Windows 98 doesn't send the SURPRISE_REMOVAL request, so this REMOVE IRP may
  // be the first indication you have that the device has disappeared. Calling
  // StopDevice allows you to release all your I/O resources in case you didn't
  // get an earlier IRP that caused you to release them. Calling AbortRequests
  // causes you to complete any queued IRPs and to start rejecting any new IRPs.

  // Cancel any queued IRPs and start rejecting new ones
  // TODO If you use multiple queues, code this instead:
  //	AbortAllRequests(pdx->queues, arraysize(pdx->queues), STATUS_DELETE_PENDING);
  AbortRequests(&pdx->dqReadWrite, STATUS_DELETE_PENDING);
  AbortPendingIoctls(pdx, STATUS_DELETE_PENDING);

  // Disable all device interfaces. This triggers PnP notifications that will
  // allow apps to close their handles.
  DeregisterAllInterfaces(pdx);

  // Release our I/O resources
  StopDevice(fdo, pdx->state == WORKING);

  KdPrint((DRIVERNAME " - To REMOVED from %s\n", statenames[pdx->state]));
  pdx->state = REMOVED;

  // We pass this request to the lower layers now that we've done our work.
  // Let lower-level drivers handle this request. Ignore whatever
  // result eventuates.
  NTSTATUS status = DefaultPnpHandler(fdo, Irp);

  // The PnP dispatch routine acquired the remove lock. We now call the special
  // function IoReleaseRemoveLockAndWait to release that lock reference and wait
  // until all references to the lock are released. Once the IoReleaseRemoveLockAndWait
  // routine returns, any subsequent call to IoAcquireRemoveLock will elicit a
  // STATUS_DELETE_PENDING status to indicate that device removal is under way.

  // Wait for all claims against this device to vanish before removing
  // the device object.
  IoReleaseRemoveLockAndWait(&pdx->RemoveLock, Irp);

  // Remove the device object
  RemoveDevice(fdo);

  return status;                                           // Lower-level completed IoStatus already
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP start device request.                                  }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleStartDevice(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_START_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  NTSTATUS status = ForwardAndWait(fdo, Irp);
  if (!NT_SUCCESS(status))
    return CompleteRequest(Irp, status);

  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  PCM_PARTIAL_RESOURCE_LIST raw;
  if (stack->Parameters.StartDevice.AllocatedResources)
    raw = &stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList;
  else
    raw = NULL;

  PCM_PARTIAL_RESOURCE_LIST translated;
  if (stack->Parameters.StartDevice.AllocatedResourcesTranslated)
    translated = &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList;
  else
    translated = NULL;

  #if DBG
    if (raw)
    {
      KdPrint((DRIVERNAME " - Resources:\n"));
      ShowResources(raw);
    }
    if (translated)
    {
      KdPrint((DRIVERNAME " - Translated Resources:\n"));
      ShowResources(translated);
    }
  #endif

  status = StartDevice(fdo, raw, translated);

  // While we were in the stopped state, we were stalling incoming requests.
  // Now we can release any pending IRPs and start processing new ones
  if (NT_SUCCESS(status))
  {                                                        // If we started okay
    // Enable all registered device interfaces.
    EnableAllInterfaces(pdx, TRUE);

    KdPrint((DRIVERNAME " - To WORKING from %s\n", statenames[pdx->state]));
    pdx->state = WORKING;
    // TODO If you use multiple queues, code this instead:
    //	AllowAllRequests(pdx->queues, arraysize(pdx->queues));
    //	RestartAllRequests(pdx->queues, arraysize(pdx->queues), fdo);
    AllowRequests(&pdx->dqReadWrite);                      // In case we got a bogus STOP
    RestartRequests(&pdx->dqReadWrite, fdo);
  }

  return CompleteRequest(Irp, status);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP stop device request.                                   }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleStopDevice(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_STOP_DEVICE);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // We expect the system to send us a QUERY_STOP before it sends us a STOP, so we
  // should already be in the PENDINGSTOP state with all of our queues stalled.
  // There is, however, a bug in Windows 98 such that we can sometimes get a STOP
  // (without a QUERY_STOP) instead of a REMOVE. You need to take some action at
  // this point that causes you to reject any new IRPs, but you mustn't really remove
  // your device object or do the other things you do when you really receive a REMOVE request.

  // We're supposed to always get a query before we're stopped, so
  // we should already be in the PENDINGSTOP state. There's a Win98 bug that
  // can sometimes cause us to get a STOP instead of a REMOVE, in which case
  // we should start rejecting IRPs
  if (pdx->state != PENDINGSTOP)
  {                                                        // If no previous query
    KdPrint((DRIVERNAME " - STOP with no previous QUERY_STOP!\n"));
    // TODO If you use multiple queues, code this instead:
    //	AbortAllRequests(pdx->queues, arraysize(pdx->queues, STATUS_DELETE_PENDING);
    AbortRequests(&pdx->dqReadWrite, STATUS_DELETE_PENDING);
    AbortPendingIoctls(pdx, STATUS_DELETE_PENDING);
  }

  // StopDevice is the helper function that deconfigures the device
  StopDevice(fdo, pdx->state == WORKING);
  KdPrint((DRIVERNAME " - To STOPPED from %s\n", statenames[pdx->state]));

  // We now enter the STOPPED state. We're in almost the same situation as we
  // were when AddDevice was done. That is, all queues are stalled, and the device
  // has no I/O resources. The only difference is that we've left our registered
  // interfaces enabled, which means that applications will not have received removal
  // notifications and will leave their handles open. Applications can also open
  // new handles in this situation. Both aspects are just as they should be, because
  // the stop condition won't last long.
  pdx->state = STOPPED;

  // The last thing we do to handle IRP_MN_STOP_DEVICE is pass the request down
  // to the lower layers of the driver hierarchy.
  return DefaultPnpHandler(fdo, Irp);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Handle PNP surprise removal request.                              }
//{            In response to the surprise removal request, a device driver      }
//{            should disable any registered interfaces. This will give          }
//{            applications a chance to close handles to your device if they're  }
//{            on the lookout for some PnP notifications. Then the driver should }
//{            release I/O resources and pass the request down.                  }
//{ Notes    : Windows 98 never sends an IRP_MN_SURPRISE_REMOVAL request.        }
//{            Consequently, a WDM driver needs to treat an unexpected           }
//{            IRP_MN_REMOVE_DEVICE as indicating surprise removal. The code     }
//{            accomplish that by calling AbortRequests and StopDevice when they }
//{            get this IRP out of the blue.                                     }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS HandleSurpriseRemoval(IN PDEVICE_OBJECT fdo, IN PIRP Irp)
{
  ASSERT(IoGetCurrentIrpStackLocation(Irp)->MinorFunction == IRP_MN_SURPRISE_REMOVAL);
  Irp->IoStatus.Status = STATUS_SUCCESS;                   // Flag that we handled this IRP
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // Cancel any queued IRPs and start rejecting new ones
  // TODO If you use multiple queues, code this instead:
  //	AbortAllRequests(pdx->queues, arraysize(pdx->queues, STATUS_DELETE_PENDING);
  AbortRequests(&pdx->dqReadWrite, STATUS_DELETE_PENDING);
  AbortPendingIoctls(pdx, STATUS_DELETE_PENDING);
  EnableAllInterfaces(pdx, FALSE);
  KdPrint((DRIVERNAME " - To SURPRISEREMOVED from %s\n", statenames[pdx->state]));

  BOOLEAN oktouch = pdx->state == WORKING;
  pdx->state = SURPRISEREMOVED;
  StopDevice(fdo, oktouch);

  return DefaultPnpHandler(fdo, Irp);
}


#if DBG
  //{ **************************************************************************** }
  //{ Params   : <list>  Partial resource list                                     }
  //{ Return   : -
  //{                                                                              }
  //{ Descript : Show resource.                                                    }
  //{ Notes    :                                                                   }
  //{ **************************************************************************** }
  #pragma PAGEDCODE
  VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list)
  {
    PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list->PartialDescriptors;
    ULONG nres = list->Count;
    ULONG i;

    for (i = 0; i < nres; ++i, ++resource)
    {                                                        // For each resource
      ULONG type = resource->Type;
      static char* name[] =
      {
        "CmResourceTypeNull",
        "CmResourceTypePort",
        "CmResourceTypeInterrupt",
        "CmResourceTypeMemory",
        "CmResourceTypeDma",
        "CmResourceTypeDeviceSpecific",
        "CmResourceTypeBusNumber",
        "CmResourceTypeDevicePrivate",
        "CmResourceTypeAssignedResource",
        "CmResourceTypeSubAllocateFrom",
      };

      KdPrint(("    type %s", type < arraysize(name) ? name[type] : "unknown"));
      switch (type)
      {                                                      // Select on resource type
        case CmResourceTypePort:
        case CmResourceTypeMemory:
          KdPrint((" start %8X%8.8lX length %X\n",
                  resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,
                  resource->u.Port.Length));
          break;
        case CmResourceTypeInterrupt:
          KdPrint(("  level %X, vector %X, affinity %X\n",
                  resource->u.Interrupt.Level, resource->u.Interrupt.Vector,
                  resource->u.Interrupt.Affinity));
          break;
        case CmResourceTypeDma:
          KdPrint(("  channel %d, port %X\n",
                  resource->u.Dma.Channel, resource->u.Dma.Port));
      }
    }
  }
#endif
