//{ **************************************************************************** }
//{ FileName............: CONTROL.CPP                                            }
//{ Project.............: FLEXCOP                                                }
//{ Author(s)...........: M.Majoor                                               }
//{ Original contents...: Walter Oney ('Programming the Windows Driver Model')   }
//{ Version.............: 1.01                                                   }
//{ ---------------------------------------------------------------------------- }
//{ IOCTL handlers for FLEXCOP driver.                                           }
//{                                                                              }
//{ Version  Date      Comment                                                   }
//{ 1.00     20050410  - Initial release based on SAA7146A driver V2.02          }
//{ 1.01     20050428  - Minor changes to compile with Windows Server 2003 DDK   }
//{ **************************************************************************** }


#include "stddcls.h"
#include "driver.h"
#include "ioctls.h"
#include "version.h"


//{ **************************************************************************** }
//{ Descript : Forwards.                                                         }
//{ **************************************************************************** }
NTSTATUS CacheControlRequest   (PDEVICE_EXTENSION pdx,  PIRP Irp, PIRP* pIrp);
VOID     OnCancelPendingIoctl  (PDEVICE_OBJECT    fdo,  PIRP Irp);
NTSTATUS OnCompletePendingIoctl(PDEVICE_OBJECT    junk, PIRP Irp, PDEVICE_EXTENSION pdx);


//{ **************************************************************************** }
//{ Params   : -                                                                 }
//{ Return   :  -                                                                 }
//{                                                                              }
//{ Descript : Update global variables.                                          }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
VOID UpdateGlobalVariables(PDEVICE_EXTENSION pdx)
{
  // Update 'decision' variables
  if (pdx->Dma1Sub0 > pdx->Dma1Sub1)
  {
    pdx->Dma1SubLargest = pdx->Dma1Sub0;
    pdx->Dma1SubLargestOrder[0] = 1;
    pdx->Dma1SubLargestOrder[1] = 0;
  }
  else
  {
    pdx->Dma1SubLargest = pdx->Dma1Sub1;
    pdx->Dma1SubLargestOrder[0] = 0;
    pdx->Dma1SubLargestOrder[1] = 1;
  }
  if (pdx->Dma2Sub0 > pdx->Dma2Sub1)
  {
    pdx->Dma2SubLargest = pdx->Dma2Sub0;
    pdx->Dma2SubLargestOrder[0] = 1;
    pdx->Dma2SubLargestOrder[1] = 0;
  }
  else
  {
    pdx->Dma2SubLargest = pdx->Dma2Sub1;
    pdx->Dma2SubLargestOrder[0] = 0;
    pdx->Dma2SubLargestOrder[1] = 1;
  }
  KdPrint((DRIVERNAME " - UpdateGlobalVariables -> Dma1SubLargest: $%8.8x\n", pdx->Dma1SubLargest));
  KdPrint((DRIVERNAME " - UpdateGlobalVariables -> Dma2SubLargest: $%8.8x\n", pdx->Dma2SubLargest));
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Dispatcher of IRP_MJ_DEVICE_CONTROL requests.                     }
//{ Notes    : Called through a user-mode DeviceIoControl API with the following }
//{            prototype:                                                        }
//{             result = DeviceIoControl(Handle, Code, InputData, InputLength,   }
//{                        OutputData, OutputLength, &Feedback, &Overlapped);    }
//{            Handle (HANDLE) is an open handle open to the device. You obtain  }
//{            this handle by calling CreateFile in the following manner:        }
//{             Handle = CreateFile("\\\\.\\IOCTL", GENERIC_READ | GENERIC_WRITE,}
//{                                 0, NULL, OPEN_EXISTING, flags, NULL);        }
//{             if (Handle == INVALID_HANDLE_VALUE)                              }
//{               <error>                                                        }
//{             ...                                                              }
//{             CloseHandle(Handle);                                             }
//{ **************************************************************************** }
#pragma PAGEDCODE
NTSTATUS DispatchControl(PDEVICE_OBJECT fdo, PIRP Irp)
{
  // You can be sure of being called at PASSIVE_LEVEL, so there's no particular reason
  // for a simple dispatch function to be anywhere but paged memory.
  PAGED_CODE();

  // Get the device extension where all 'user' data is
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // Like other dispatch functions, this one needs to claim the remove lock while
  // it does its work. That prevents the device object from disappearing out from
  // underneath us because of a PnP event.
  NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
  if (!NT_SUCCESS(status))
    return CompleteRequest(Irp, status, 0);
  ULONG info = 0;
  BOOLEAN Escape;

  // The next few statements extract the function code and buffer sizes from the
  // parameters union in the I/O stack. You often need these values no matter which
  // specific IOCTL you're processing, so I find it easier to always include these
  // statements in the function.
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  ULONG              cbin  = stack->Parameters.DeviceIoControl.InputBufferLength;
  ULONG              cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
  ULONG              code  = stack->Parameters.DeviceIoControl.IoControlCode;

// KdPrint((DRIVERNAME " - DispatchControl - code $%x\n", IOCTL_GET_STATUS));
  switch (code)
  {                                                        // Process request
    case IOCTL_GET_VERSION:                                // Code == 0x800
    {                                                      // GET VERSION
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_GET_VERSION)\n"));
      if (cbout < sizeof(GETVERSION))
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      PGETVERSION pversion = (PGETVERSION) Irp->AssociatedIrp.SystemBuffer;
      info = sizeof(GETVERSION);
      pversion->majorversion = VERMAJOR;
      pversion->minorversion = VERMINOR;
      pversion->build = BUILD;
      break;
    }

    case IOCTL_GET_STATUS:                                 // Code == 0x801
    {                                                      // GET STATUS
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_GET_STATUS)\n"));
      if ((cbin < sizeof(ULONG)) || (cbout < sizeof(GETSTATUS)))
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PULONG pindex = (PULONG) Irp->AssociatedIrp.SystemBuffer;
      ULONG  index = *pindex;
      PGETSTATUS pstatus = (PGETSTATUS) Irp->AssociatedIrp.SystemBuffer;
      info = sizeof(GETSTATUS);
      pstatus->interrupts    = pdx->InterruptCount;
      pstatus->isr           = pdx->ISR;
      pstatus->fifoOverflows = pdx->fifoOverflows;
      pstatus->buffersize = 0;                           // In case we DON'T have a valid buffer
//      if ((index >= 0) && (index < DMABUFFERS))  V1.01
      if (index < DMABUFFERS)
      {
        if (pdx->dmaBuffers[index].bufferId >= 0)
        {
          pstatus->vaBuffer   = pdx->dmaBuffers[index].vaBuffer;
          pstatus->paBuffer   = pdx->dmaBuffers[index].paBuffer;
          pstatus->buffersize = pdx->dmaBuffers[index].buffersize;
        }
      }
      break;
    }

    case IOCTL_FLEXCOP_READ:                               // Code == 0x802
    {                                                      // FLEXCOP READ
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FLEXCOP_READ)\n"));
      // A FLEXCOP READ consists of an address to read from.
      // For simplicity we use the same type for source and destination.
      if ((cbin < sizeof(ULONG)) || (cbout < sizeof(ULONG)))
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PULONG poffset = (PULONG) Irp->AssociatedIrp.SystemBuffer;   // Get pointer to buffer
      if (((*poffset%4) != 0) || (*poffset >= pdx->memsize))       // Address MUST be valid (4 byte boundary)
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      info = sizeof(ULONG);
      ULONG value = READ_REGISTER_ULONG((PULONG) (pdx->membase + *poffset));
      ULONG value2 = value & 0xFFFFFFFC;
      // Check for values we need to retain
      if (*poffset == FLEXCOP_DMA1SUB0)
      {
        pdx->Dma1Sub0 = value2;
        UpdateGlobalVariables(pdx);
      }
      if (*poffset == FLEXCOP_DMA1SUB1)
      {
        pdx->Dma1Sub1 = value2;
        UpdateGlobalVariables(pdx);
      }
      if (*poffset == FLEXCOP_DMA2SUB0)
      {
        pdx->Dma2Sub0 = value2;
        UpdateGlobalVariables(pdx);
      }
      if (*poffset == FLEXCOP_DMA2SUB1)
      {
        pdx->Dma2Sub1 = value2;
        UpdateGlobalVariables(pdx);
      }

      *poffset = value;
      break;
    }

    case IOCTL_FLEXCOP_WRITE:                              // Code == 0x803
    {                                                      // FLEXCOP WRITE
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FLEXCOP_WRITE)\n"));
      // A FLEXCOP WRITE consists of an address to write to and data.
      // For simplicity we use the same type for address and data.
      if (cbin < sizeof(FLEXCOPWRITE))
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PFLEXCOPWRITE poffset = (PFLEXCOPWRITE) Irp->AssociatedIrp.SystemBuffer;    // Get pointer to buffer
      if (((poffset->address%4) != 0) || (poffset->address >= pdx->memsize))      // Address MUST be valid (4 byte boundary)
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // Check for values we need to retain
      ULONG value = poffset->data & 0xFFFFFFFC;
      if (poffset->address == FLEXCOP_DMA1SUB0)
      {
        pdx->Dma1Sub0 = value;
        UpdateGlobalVariables(pdx);
      }
      if (poffset->address == FLEXCOP_DMA1SUB1)
      {
        pdx->Dma1Sub1 = value;
        UpdateGlobalVariables(pdx);
      }
      if (poffset->address == FLEXCOP_DMA2SUB0)
      {
        pdx->Dma2Sub0 = value;
        UpdateGlobalVariables(pdx);
      }
      if (poffset->address == FLEXCOP_DMA2SUB1)
      {
        pdx->Dma2Sub1 = value;
        UpdateGlobalVariables(pdx);
      }

      WRITE_REGISTER_ULONG((PULONG) (pdx->membase + poffset->address),
                             poffset->data);
      break;
    }

    // Note: only ONE can call this at any time (there is only one 'NotifyIrp') !!!!
    case IOCTL_WAIT_NOTIFY:                                // Code == 0x804
    {
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_WAIT_NOTIFY)\n"));
      if (cbout < sizeof(ULONG))
        status = STATUS_INVALID_PARAMETER;
      else
        status = CacheControlRequest(pdx, Irp, &pdx->NotifyIrp);
      break;
    }

    case IOCTL_GENERATE_EVENT:                             // Code == 0x805
    {
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_GENERATE_EVENT)\n"));
      if (cbin < sizeof(ULONG))
      {
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PIRP nfyirp = UncacheControlRequest(pdx, &pdx->NotifyIrp);
      if (nfyirp)
      {                                                    // If complete notification IRP
        *(PULONG) nfyirp->AssociatedIrp.SystemBuffer = *(PULONG) Irp->AssociatedIrp.SystemBuffer;
        CompleteRequest(nfyirp, STATUS_SUCCESS, sizeof(ULONG));
      }
      else
        status = STATUS_UNSUCCESSFUL;
      break;
    }

    case IOCTL_DMA_ALLOCATE:                               // Code == 0x806
    {                                                      // DMA ALLOCATE
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_ALLOCATE)\n"));
      if ((cbin < sizeof(ULONG)) || (cbout < sizeof(DMABUFFER)))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_ALLOCATE) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      // Find a free record were we can store our DMA buffer data
      ULONG freebuf;
      freebuf = 0;
      while ((pdx->dmaBuffers[freebuf].bufferId >= 0) && (freebuf < DMABUFFERS))
        freebuf++;
      if (freebuf >= DMABUFFERS)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_ALLOCATE) -> all DMA buffers already in use\n"));
        status = STATUS_INSUFFICIENT_RESOURCES;
        break;
      }

      PULONG size = (PULONG) Irp->AssociatedIrp.SystemBuffer;
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_ALLOCATE) -> buffer %d, allocating %d bytes\n", freebuf, *size));
      // Allocate the buffer
      pdx->dmaBuffers[freebuf].vaBuffer =
        (*pdx->AdapterObject->DmaOperations->AllocateCommonBuffer)
          (pdx->AdapterObject, *size, &pdx->dmaBuffers[freebuf].paBuffer, FALSE);
      if (!pdx->dmaBuffers[freebuf].vaBuffer)
      {                                                // If we can't create DMA buffer
        KdPrint((DRIVERNAME " - Unable to allocate common DMA buffer\n"));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      pdx->dmaBuffers[freebuf].bufferId   = freebuf;
      pdx->dmaBuffers[freebuf].buffersize = *size;

      PDMABUFFER pdmabuffer = (PDMABUFFER) Irp->AssociatedIrp.SystemBuffer;
      info = sizeof(DMABUFFER);
      pdmabuffer->bufferId   = pdx->dmaBuffers[freebuf].bufferId;
      pdmabuffer->vaBuffer   = pdx->dmaBuffers[freebuf].vaBuffer;
      pdmabuffer->paBuffer   = pdx->dmaBuffers[freebuf].paBuffer;
      pdmabuffer->buffersize = pdx->dmaBuffers[freebuf].buffersize;
      break;
    }

    case IOCTL_DMA_RELEASE:                                // Code == 0x807
    {                                                      // DMA RELEASE
      if (cbin < sizeof(ULONG))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_RELEASE) -> invalid parameter\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PULONG index = (PULONG) Irp->AssociatedIrp.SystemBuffer;
      // Must be valid buffer
//      if ((*index < 0) || (*index >= DMABUFFERS))  // V1.01
      if (*index >= DMABUFFERS)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_RELEASE) -> buffer %d does not exist\n", *index));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // DMA buffer must be in use to be de-allocated
      if (pdx->dmaBuffers[*index].bufferId < 0)
      {
//        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_RELEASE) -> buffer %d never allocated\n", *index));
      }
      else
      {                                                    // Release it
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_RELEASE) -> releasing buffer %d\n", *index));
        // We have to check if the DMA memory we will be releasing is used by FIFO buffers.
        // If so we need to deactivate/deallocate the FIFO buffers too.
        BOOLEAN addressInRange;
        PUCHAR  addressStart = (PUCHAR) pdx->dmaBuffers[*index].vaBuffer;
        PUCHAR  addressEnd   = addressStart + pdx->dmaBuffers[*index].buffersize;
        PUCHAR  addressCheck;
        int Loop;
        for (Loop = 0; Loop < FIFOBUFFERS; Loop++)
        {
          if (pdx->fifoBuffers[Loop].bufferId >= 0)
          {
            // Check if FIFO address in range of DMA addresses
            addressInRange = FALSE;
            addressCheck = (PUCHAR)pdx->fifoBuffers[Loop].vaBuffer;
            if ((addressCheck >= addressStart) ||
                (addressCheck <= addressEnd ))
              addressInRange = TRUE;
            addressCheck += pdx->fifoBuffers[Loop].bufferSize;
            if ((addressCheck >= addressStart) ||
                (addressCheck <= addressEnd ))
              addressInRange = TRUE;
            // Deactivate/deallocate FIFO
            if (addressInRange == TRUE)
            {
              KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_RELEASE) -> also releasing FIFO buffer %d\n", Loop));
              ExFreePool(pdx->fifoBuffers[Loop].vaBuffer);
              pdx->fifoBuffers[Loop].bufferId = -1;
              // Disable IRQ link -> this means that even other FIFO's which are still valid are disabled!
              pdx->irqBuffers[pdx->fifoBuffers[Loop].irqBuffer].fifoActive = FALSE;
            }
          }
        }
        // Release DMA memory
        (*pdx->AdapterObject->DmaOperations->FreeCommonBuffer)
          (pdx->AdapterObject, pdx->dmaBuffers[*index].buffersize, pdx->dmaBuffers[*index].paBuffer,
          pdx->dmaBuffers[*index].vaBuffer, FALSE);
        pdx->dmaBuffers[*index].bufferId = -1;
      }
      break;
    }

    case IOCTL_DMA_READ:                                   // Code == 0x808
    {                                                      // DMA READ
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_READ)\n"));
      if (cbin < sizeof(TRANSFERBUFFER))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_READ) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      PTRANSFERBUFFER transferBuffer = (PTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      // Must be valid buffer
      if ((transferBuffer->bufferId < 0) || (transferBuffer->bufferId >= DMABUFFERS))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_READ) -> buffer %d does not exist\n", transferBuffer->bufferId));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // DMA buffer must be in use to be read
      if (pdx->dmaBuffers[transferBuffer->bufferId].bufferId < 0)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_READ) -> buffer %d to be read is not allocated\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }

      ULONG transferIndexEnd = transferBuffer->bufferSourceIndex + transferBuffer->bufferTransferLength;
      // Check for size
      if (transferIndexEnd > pdx->dmaBuffers[transferBuffer->bufferId].buffersize)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_READ) -> buffer %d data request exceeds available size\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      // Copy data to target buffer
      RtlCopyMemory(transferBuffer->bufferTransfer + transferBuffer->bufferTargetIndex,
                    (PUCHAR)pdx->dmaBuffers[transferBuffer->bufferId].vaBuffer + transferBuffer->bufferSourceIndex,
                    transferBuffer->bufferTransferLength);
      break;
    }

    case IOCTL_DMA_WRITE:                                  // Code == 0x809
    {                                                      // DMA WRITE
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_WRITE)\n"));
      if (cbin < sizeof(TRANSFERBUFFER))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_WRITE) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PTRANSFERBUFFER transferBuffer = (PTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;

      // Must be valid buffer
      if ((transferBuffer->bufferId < 0) || (transferBuffer->bufferId >= DMABUFFERS))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_WRITE) -> buffer %d does not exist\n", transferBuffer->bufferId));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // DMA buffer must be in use to be read
      if (pdx->dmaBuffers[transferBuffer->bufferId].bufferId < 0)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_WRITE) -> buffer %d to be written is not allocated\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }

      ULONG transferIndexEnd = transferBuffer->bufferTargetIndex + transferBuffer->bufferTransferLength;
      // Check for size
      if (transferIndexEnd > pdx->dmaBuffers[transferBuffer->bufferId].buffersize)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_DMA_WRITE) -> buffer %d data request exceeds available size\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      // Copy data to target buffer
      RtlCopyMemory((PUCHAR)pdx->dmaBuffers[transferBuffer->bufferId].vaBuffer + transferBuffer->bufferTargetIndex,
                    transferBuffer->bufferTransfer + transferBuffer->bufferSourceIndex,
                    transferBuffer->bufferTransferLength);
      break;
    }

    case IOCTL_FIFO_ALLOCATE:                              // Code == 0x80A
    {                                                      // FIFO ALLOCATE
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE)\n"));
      if ((cbin < sizeof(FIFOTRANSFERBUFFER)) || (cbout < sizeof(FIFOTRANSFERBUFFER)))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PFIFOTRANSFERBUFFER transferBuffer = (PFIFOTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      LONG    freebuf;
      int     Loop;
      int     Loop2;
      PUCHAR  transferAddressEnd;
      BOOLEAN validAddress = FALSE;
      // We have to check if the memory we will be buffering does actually 'exist'.
      // Since the driver only allocates DMA memory besides the FIFO memory, we only
      // have to check if DMA memory has been allocated in the indicates memory range.
      // All sources for each possible interrupt
      for (Loop2 = 0; Loop2 < 2; Loop2++)
      {
        // Only check assigned 'interrupts' addresses
        if (transferBuffer->bufferTransfer[Loop2] != 0)
        {
          transferAddressEnd = (PUCHAR)transferBuffer->bufferTransfer[Loop2] + transferBuffer->bufferTransferLength;
          validAddress = FALSE;
          Loop = 0;
          while ((Loop < DMABUFFERS) && (validAddress == FALSE))
          {
            if (pdx->dmaBuffers[Loop].bufferId >= 0)         // If in use (just what we need)
            {
              if ( (pdx->dmaBuffers[Loop].vaBuffer <= transferBuffer->bufferTransfer[Loop2]) &&
                  (((PUCHAR)pdx->dmaBuffers[Loop].vaBuffer + pdx->dmaBuffers[Loop].buffersize) >= transferAddressEnd))
              {
                validAddress = TRUE;
                KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE) -> valid memory range found for IRQ %d)\n", Loop2));
              }
            }
            Loop++;
          }
          if (validAddress == FALSE)
            break;
        }
      }
      if (validAddress == FALSE)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE) -> invalid memory range (must be IOCTL_DMA_ALLOCATE allocated memory)\n"));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      // Find a number of free records for the FIFO buffer data
      freebuf = 0;
      while ((pdx->fifoBuffers[freebuf].bufferId >= 0) && ((freebuf + transferBuffer->buffers) <= FIFOBUFFERS))
        freebuf++;
      if ((freebuf + transferBuffer->buffers) > FIFOBUFFERS)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE) -> requested number of FIFO buffers not available\n"));
        status = STATUS_INSUFFICIENT_RESOURCES;
        break;
      }
      transferBuffer->bufferId = freebuf;                             // Keep copy of first index
      Escape = FALSE;
      for (Loop = 0; Loop < transferBuffer->buffers; Loop++)
      {
        // All sources for each possible interrupt
        for (Loop2 = 0; Loop2 < 2; Loop2++)
        {
          pdx->fifoBuffers[freebuf].vaBufferSource[Loop2] = transferBuffer->bufferTransfer[Loop2];
        }
        pdx->fifoBuffers[freebuf].bufferSize   = transferBuffer->bufferTransferLength;
        pdx->fifoBuffers[freebuf].vaBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferBuffer->bufferTransferLength);
        pdx->fifoBuffers[freebuf].writeTag = FALSE;
        pdx->fifoBuffers[freebuf].bufferIrqsActiveReceived = 0;
        pdx->fifoBuffers[freebuf].bufferOverflows = 0;
        if (!pdx->fifoBuffers[freebuf].vaBuffer)
        {                                                // If we can't allocate FIFO buffer
          // We have to release all buffers we allocated for this call
          while (freebuf >= transferBuffer->bufferId)
          {
            if (pdx->fifoBuffers[freebuf].bufferId >= 0)         // If in use
              ExFreePool(pdx->fifoBuffers[freebuf].vaBuffer);    // Release the allocated memory
            pdx->fifoBuffers[freebuf].bufferId = -1;
            freebuf--;
          }
          KdPrint((DRIVERNAME " - Unable to allocate FIFO buffers\n"));
          status = STATUS_INSUFFICIENT_RESOURCES;
          Escape = TRUE;
          break;
        }
        pdx->fifoBuffers[freebuf].bufferId = 1;                 // Indicate in use
        freebuf++;
      }
      // Since the for..loop might have exit with an error we check this
      if (Escape)
        break;
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_ALLOCATE) -> allocated %d buffers of %d bytes (FIFO %d)\n", transferBuffer->buffers, transferBuffer->bufferTransferLength, transferBuffer->bufferId));
      info = sizeof(FIFOTRANSFERBUFFER);
      break;
    }

    case IOCTL_FIFO_RELEASE:                               // Code == 0x80B
    {                                                      // FIFO RELEASE
      if ((cbin < sizeof(FIFOTRANSFERBUFFER)) || (cbout < sizeof(FIFOTRANSFERBUFFER)))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_RELEASE) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PFIFOTRANSFERBUFFER transferBuffer = (PFIFOTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      LONG Loop;
      // Make sure loop indexes are within range (if this data is returned then we also return used range)
      if (transferBuffer->bufferId < 0)
        transferBuffer->bufferId = 0;
      if (transferBuffer->bufferId >= FIFOBUFFERS)
        transferBuffer->bufferId = FIFOBUFFERS-1;
      if (transferBuffer->buffers > FIFOBUFFERS)
        transferBuffer->buffers = FIFOBUFFERS;
      if ((transferBuffer->bufferId + transferBuffer->buffers) > FIFOBUFFERS)
        transferBuffer->buffers = FIFOBUFFERS - transferBuffer->bufferId;
      for (Loop = transferBuffer->bufferId; Loop < (transferBuffer->bufferId + transferBuffer->buffers); Loop++)
      {
        if (pdx->fifoBuffers[Loop].bufferId >= 0)
          ExFreePool(pdx->fifoBuffers[Loop].vaBuffer);
        pdx->fifoBuffers[Loop].bufferId = -1;
        // Disable IRQ link -> this means that even other FIFO's which are still valid are disabled!
        pdx->irqBuffers[pdx->fifoBuffers[Loop].irqBuffer].fifoActive = FALSE;
      }
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_RELEASE) -> releasing buffers %d to %d\n", transferBuffer->bufferId, transferBuffer->bufferId + transferBuffer->buffers - 1));
      info = sizeof(FIFOTRANSFERBUFFER);
      break;
    }

    case IOCTL_FIFO_READ:                                  // Code == 0x80C
    {                                                      // FIFO READ
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_READ)\n"));
      if (cbin < sizeof(FIFOTRANSFERBUFFER) || (cbout < sizeof(FIFOTRANSFERBUFFER)))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_READ) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PFIFOTRANSFERBUFFER transferBuffer = (PFIFOTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      // Must be valid buffer
      if ((transferBuffer->bufferId < 0) || (transferBuffer->bufferId >= FIFOBUFFERS))                        // MUST be valid index
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_READ) -> buffer %d does not exist\n", transferBuffer->bufferId));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // Check for in use
      if (pdx->fifoBuffers[transferBuffer->bufferId].bufferId < 0)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_READ) -> buffer %d never allocated\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      // Check for size
      if (transferBuffer->bufferTransferLength < pdx->fifoBuffers[transferBuffer->bufferId].bufferSize)
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_FIFO_READ) -> target buffer to small for data FIFO buffer %d\n", transferBuffer->bufferId));
        status = STATUS_UNSUCCESSFUL;
        break;
      }
      // Copy data to target buffer
      PULONG source = (PULONG)pdx->fifoBuffers[transferBuffer->bufferId].vaBuffer;
      PVOID target = transferBuffer->bufferTransfer[0];    // Use first index
      RtlCopyMemory(target, source, pdx->fifoBuffers[transferBuffer->bufferId].bufferSize);
      // Reset write tag and update returned data
      transferBuffer->bufferTransferLength = pdx->fifoBuffers[transferBuffer->bufferId].bufferSize;
      transferBuffer->bufferOrder          = pdx->fifoBuffers[transferBuffer->bufferId].bufferIrqsActiveReceived;
      transferBuffer->bufferWritten        = pdx->fifoBuffers[transferBuffer->bufferId].writeTag;
      transferBuffer->bufferOverflows      = pdx->fifoBuffers[transferBuffer->bufferId].bufferOverflows;
      transferBuffer->bufferIrqs           = pdx->irqBuffers[pdx->fifoBuffers[transferBuffer->bufferId].irqBuffer].irqsActiveReceived;
      transferBuffer->bufferIrqOverflows   = pdx->irqBuffers[pdx->fifoBuffers[transferBuffer->bufferId].irqBuffer].fifoOverflows;
      pdx->fifoBuffers[transferBuffer->bufferId].writeTag = FALSE;
      info = sizeof(FIFOTRANSFERBUFFER);
      break;
    }

    case IOCTL_IRQ_WRITE:                                  // Code == 0x80D
    {                                                      // IRQ WRITE HANDLING
      if (cbin < sizeof(IRQTRANSFERBUFFER))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PIRQTRANSFERBUFFER transferBuffer = (PIRQTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      if ((transferBuffer->IrqId < 0) || (transferBuffer->IrqId >= IRQBUFFERS))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid IRQ ID parameter\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      // Check the parameters passed on. Only the active parameters are checked.
      if (transferBuffer->IrqInformation.active)           // Only check if setting active
      {
        if (transferBuffer->IrqInformation.signalActive)
        {
          if (transferBuffer->IrqInformation.signalRegister%4 != 0)
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid SIGNALREGISTER parameters (not dword aligned)\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          if (transferBuffer->IrqInformation.signalRegister >= pdx->memsize)
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid SIGNALREGISTER parameters (outside range)\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
        }
        // The most extensive check is for the FIFO. We must check if the FIFO buffers are available.
        if (transferBuffer->IrqInformation.fifoActive)
        {
          // First the very simple range checks
          if ((transferBuffer->IrqInformation.fifoBufferFirst < 0) || (transferBuffer->IrqInformation.fifoBufferFirst > FIFOBUFFERS))
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid FIFOBUFFERFIRST parameter\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          if ((transferBuffer->IrqInformation.fifoBufferLast < 0) || (transferBuffer->IrqInformation.fifoBufferLast > FIFOBUFFERS))
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid FIFOBUFFERLAST parameter\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          if ((transferBuffer->IrqInformation.fifoBufferPrevious < 0) || (transferBuffer->IrqInformation.fifoBufferPrevious > FIFOBUFFERS))
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid FIFOBUFFERPREVIOUS parameter\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          if (transferBuffer->IrqInformation.fifoBufferFirst > transferBuffer->IrqInformation.fifoBufferLast)
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid parameters: FIFOBUFFERFIRST > FIFOBUFFERLAST\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          if ((transferBuffer->IrqInformation.fifoBufferPrevious < transferBuffer->IrqInformation.fifoBufferFirst) ||
              (transferBuffer->IrqInformation.fifoBufferPrevious > transferBuffer->IrqInformation.fifoBufferLast))
          {
            KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid FIFOBUFFERPREVIOUS parameter\n"));
            status = STATUS_INVALID_PARAMETER;
            break;
          }
          Escape = FALSE;
          int Loop;
          for (Loop = transferBuffer->IrqInformation.fifoBufferFirst; Loop <= transferBuffer->IrqInformation.fifoBufferLast; Loop++)
          {
            if (pdx->fifoBuffers[Loop].bufferId < 0)
            {
              KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) -> invalid FIFO parameters (FIFO not allocated)\n"));
              status = STATUS_INVALID_PARAMETER;
              Escape = TRUE;
              break;
            }
          }
          // In case the for..loop was exit with an error
          if (Escape)
            break;
          // Set FIFO -> IRQ link
          for (Loop = transferBuffer->IrqInformation.fifoBufferFirst; Loop <= transferBuffer->IrqInformation.fifoBufferLast; Loop++)
            pdx->fifoBuffers[Loop].irqBuffer = (UCHAR)transferBuffer->IrqId;
        }
      }
      RtlCopyMemory(&pdx->irqBuffers[transferBuffer->IrqId], &transferBuffer->IrqInformation,  sizeof(IRQBUFFER));
      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_WRITE) writing IRQ %d information\n", transferBuffer->IrqId));
      break;
    }

    case IOCTL_IRQ_READ:                                   // Code == 0x80E
    {                                                      // IRQ READ HANDLING
      if ((cbin < sizeof(IRQTRANSFERBUFFER)) || (cbout < sizeof(IRQTRANSFERBUFFER)))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_READ) -> invalid parameter length\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }

      PIRQTRANSFERBUFFER transferBuffer = (PIRQTRANSFERBUFFER) Irp->AssociatedIrp.SystemBuffer;
      if ((transferBuffer->IrqId < 0) || (transferBuffer->IrqId >= IRQBUFFERS))
      {
        KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_READ) -> invalid IRQ ID parameter\n"));
        status = STATUS_INVALID_PARAMETER;
        break;
      }
      RtlCopyMemory(&transferBuffer->IrqInformation, &pdx->irqBuffers[transferBuffer->IrqId], sizeof(IRQBUFFER));
//      KdPrint((DRIVERNAME " - DispatchControl (IOCTL_IRQ_READ) reading IRQ %d information\n", transferBuffer->IrqId));
      info = sizeof(IRQTRANSFERBUFFER);
      break;
    }

    default:
      // It's a good idea to return a meaningful status code if you're given an IOCTL
      // operation you don't understand.
      KdPrint((DRIVERNAME " - DispatchControl - IOCTL not recognized $%x\n", code));
      status = STATUS_INVALID_DEVICE_REQUEST;
      break;
  }

  IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
  return status == STATUS_PENDING ? status : CompleteRequest(Irp, status, info);
}


//{ **************************************************************************** }
//{ Params   : <fdo>     Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Dispatcher of IRP_MJ_INTERNAL_DEVICE_CONTROL requests.            }
//{            Drivers sometimes need to talk to each other too, and they use the}
//{            related IRP_MJ_INTERNAL_DEVICE_CONTROL to do so.                  }
//{            A typical code sequence which calls this is as follows:           }
//{             ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);                     }
//{             KEVENT event;                                                    }
//{             KeInitializeEvent(&event, NotificationEvent, FALSE);             }
//{             IO_STATUS_BLOCK iostatus;                                        }
//{             PIRP Irp = IoBuildDeviceIoControlRequest(IoControlCode,          }
//{               DeviceObject, pInBuffer, cbInBuffer, pOutBuffer, cbOutBuffer,  }
//{               TRUE, &event, &iostatus);                                      }
//{             if (IoCallDriver(DeviceObject, Irp) == STATUS_PENDING)           }
//{               KeWaitForSingleObject(&event, Executive, KernelMode, FALSE,    }
//{                 NULL);                                                       }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
NTSTATUS DispatchInternalControl(PDEVICE_OBJECT fdo, PIRP Irp)
{
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  IoSkipCurrentIrpStackLocation(Irp);
  return IoCallDriver(pdx->LowerDeviceObject, Irp);
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <status>  Status to pass through                                  }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Abort pending IOCTL's.                                            }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
VOID AbortPendingIoctls(PDEVICE_EXTENSION pdx, NTSTATUS status)
{
  PAGED_CODE();
  InterlockedExchange(&pdx->IoctlAbortStatus, status);
  CleanupControlRequests(pdx, status, NULL);
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <Irp>     I/O request packet to cache                             }
//{            <pIrp>    I/O request packet to cache it in                       }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : Cache a control request.                                          }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
NTSTATUS CacheControlRequest(PDEVICE_EXTENSION pdx, PIRP Irp, PIRP* pIrp)
{
  ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

  KIRQL oldirql;
  // We use a spin lock to guard the list of pending IOCTLs and also to guard all
  // of the pointer cells that are reserved to point to the current instance of each
  // different type of asynchronous IOCTL request.
  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);

  NTSTATUS status;
  // This is where we enforce the rule—it's more of a design decision, really that
  // only one IRP of each type can be outstanding at one time.
  if (*pIrp)
    status = STATUS_UNSUCCESSFUL;                          // Something already cached here
  else
       // This if statement accommodates the fact that we may need to start failing
       // incoming IRPs at some point because of PnP or power events.
       if (pdx->IoctlAbortStatus)
         status = pdx->IoctlAbortStatus;                   // Rejecting new IRPs for some reason
       else
       {                                                   // Try to cache IRP
         // Since we'll pend this IRP for what might be a long time, we need to
         // have a cancel routine for it.
         IoSetCancelRoutine(Irp, OnCancelPendingIoctl);
         if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
           status = STATUS_CANCELLED;                      // Already cancelled
                                                           // Put this IRP on our list of pending IOCTLs. Install a completion
                                                           // routine to nullify the cache pointer. Note that AddDevice would have
                                                           // failed if there were no PDO below us, so we know there's at least
                                                           // one free stack location here.
         else
         {                                                 // If to cache it
           // Here, we've decided to go ahead and cache this IRP so that we can complete
           // it later. Since we're going to end up returning STATUS_PENDING from our
           // DispatchControl function, we need to call IoMarkIrpPending.
           IoMarkIrpPending(Irp);
           status = STATUS_PENDING;

           // We need to have a way to NULL out the cache pointer cell when we cancel
           // the IRP. It's very difficult to get context parameters into a cancel
           // routine, so we decide to set up an I/O completion routine instead.
           // We use the Parameters.Others.Argument1 slot in the stack to record
           // the cache pointer address.
           PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
           stack->Parameters.Others.Argument1 = (PVOID) pIrp;
           IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnCompletePendingIoctl, (PVOID) pdx, TRUE, TRUE, TRUE);
           // In order for the completion routine we've just installed to get called,
           // we must advance the I/O stack pointer by calling IoSetNextIrpStackLocation.
           // In this particular driver, we know there must be at least one more stack
           // location for us to use because our AddDevice function would have failed
           // if there hadn't been a driver object underneath ours. The device and file
           // object pointers that later routines need come from the then-current stack
           // location, so we must initialize them as well.
           IoSetNextIrpStackLocation(Irp);                 // So our completion routine will get called
           PFILE_OBJECT fop = stack->FileObject;
           stack = IoGetCurrentIrpStackLocation(Irp);
           stack->DeviceObject = pdx->DeviceObject;        // So IoCancelIrp can give us right ptr
           stack->FileObject   = fop;                      // For cleanup

           *pIrp = Irp;
           InsertTailList(&pdx->PendingIoctlList, &Irp->Tail.Overlay.ListEntry);
         }
       }

  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);
  return status;
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <status>  Status to pass through                                  }
//{            <fop>     File object                                             }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : Cleanup control request.                                          }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma PAGEDCODE
VOID CleanupControlRequests(PDEVICE_EXTENSION pdx, NTSTATUS status, PFILE_OBJECT fop)
{
  LIST_ENTRY cancellist;
  InitializeListHead(&cancellist);

  // Create a list of IRPs that belong to the same file object
  KIRQL oldirql;
  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);

  PLIST_ENTRY first = &pdx->PendingIoctlList;
  PLIST_ENTRY next;
  for (next = first->Flink; next != first; )
  {                                                        // For each queued IRP
    PIRP Irp = CONTAINING_RECORD(next, IRP, Tail.Overlay.ListEntry);
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

    // Follow the chain to the next IRP now (so that the next iteration of
    // the loop is properly setup whether we dequeue this IRP or not)
    PLIST_ENTRY current = next;
    next = next->Flink;

    // Skip this IRP if it's not for the same file object as the
    // current IRP_MJ_CLEANUP.
    if (fop && stack->FileObject != fop)
      continue;                                            // Not for same file object

    // Set the CancelRoutine pointer to NULL and remove the IRP from the
    // queue.
    if (!IoSetCancelRoutine(Irp, NULL))
      continue;                                            // Being cancelled right this instant
    RemoveEntryList(current);
    InsertTailList(&cancellist, current);
  }

  // Release the spin lock. We're about to undertake a potentially time-consuming
  // operation that might conceivably result in a deadlock if we keep the lock.
  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);

  // Complete the selected requests.
  while (!IsListEmpty(&cancellist))
  {                                                        // Cancel selected requests
    next = RemoveHeadList(&cancellist);
    PIRP Irp = CONTAINING_RECORD(next, IRP, Tail.Overlay.ListEntry);
    Irp->IoStatus.Status = STATUS_CANCELLED;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
  }
}


//{ **************************************************************************** }
//{ Params   : <fdo>  Functional device object                                   }
//{            <Irp>  I/O request packet                                         }
//{ Return   : -                                                                 }
//{                                                                              }
//{ Descript : On cancel callback of pending IOCTL.                              }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
VOID OnCancelPendingIoctl(PDEVICE_OBJECT fdo, PIRP Irp)
{
  KIRQL oldirql = Irp->CancelIrql;
  IoReleaseCancelSpinLock(DISPATCH_LEVEL);
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

  // Remove the IRP from whatever queue it's on
  KeAcquireSpinLockAtDpcLevel(&pdx->IoctlListLock);
  RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);

  // Complete the IRP
  Irp->IoStatus.Status = STATUS_CANCELLED;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
}


//{ **************************************************************************** }
//{ Params   : <junk>    Functional device object                                }
//{            <Irp>     I/O request packet                                      }
//{            <pdx>     Device extension                                        }
//{ Return   : <Result>  NTSTATUS                                                }
//{                                                                              }
//{ Descript : On complete callback of pending IOCTL.                            }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
NTSTATUS OnCompletePendingIoctl(PDEVICE_OBJECT junk, PIRP Irp, PDEVICE_EXTENSION pdx)
{
  KIRQL oldirql;
  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);
  PIRP* pIrp = (PIRP*) IoGetCurrentIrpStackLocation(Irp)->Parameters.Others.Argument1;
  // This statement is the point of installing a completion routine. If the IRP gets
  // cancelled, we'll eventually gain control to nullify the cache pointer.
  if (*pIrp == Irp)
    *pIrp = NULL;
  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);
  return STATUS_SUCCESS;
}


//{ **************************************************************************** }
//{ Params   : <pdx>     Device extension                                        }
//{            <pIrp>    I/O request packet                                      }
//{ Return   : <Result>  I/O request packet                                      }
//{                                                                              }
//{ Descript : Uncache control request.                                          }
//{ Notes    :                                                                   }
//{ **************************************************************************** }
#pragma LOCKEDCODE
PIRP UncacheControlRequest(PDEVICE_EXTENSION pdx, PIRP* pIrp)
{
  ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);

  KIRQL oldirql;
  KeAcquireSpinLock(&pdx->IoctlListLock, &oldirql);

  // In the normal course of events, this statement uncaches an IRP
//  PIRP Irp = (PIRP) InterlockedExchangePointer(pIrp, NULL); // V1.01
  PIRP Irp = (PIRP) InterlockedExchangePointer((volatile PVOID *)pIrp, NULL);
  if (Irp)
  {                                                        // If an IRP was cached
    // Now that we've uncached our IRP, we don't want it to be cancelled any more.
    // If IoSetCancelRoutine returns NULL, however, we know that this IRP is currently
    // in the process of being cancelled. We return a NULL IRP pointer in that case.

    // Clear the cancel pointer for this IRP. Since both we and the
    // completion routine use a spin lock, it cannot happen that this
    // IRP pointer is suddenly invalid but the cache pointer cell
    // wasn't already NULL.
    if (IoSetCancelRoutine(Irp, NULL))
    {
      RemoveEntryList(&Irp->Tail.Overlay.ListEntry);       // N.B.: a macro!!
    }
    else
      Irp = NULL;                                          // If currently being cancelled
  }

  KeReleaseSpinLock(&pdx->IoctlListLock, oldirql);
  return Irp;
}
