Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

//-------------------------------------------------------------------------
// WINNT driver for PCIVME interface from ARW Elektronik, Germany ---------
// the ioctl functions
//
// (c) 1999-2004 ARW Elektronik
//
// this source code is published under GPL (Open Source). You can use, redistrubute and
// modify it unless this header   is not modified or deleted. No warranty is given that
// this software will work like expected.
// This product is not authorized for use as critical component in life support systems
// wihout the express written approval of ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// $Log: pcivme_io.c,v $
// Revision 1.3  2004/07/24 07:07:26  klaus
// Update copyright to 2004
//
// Revision 1.2  2003/11/15 19:12:51  klaus
// Update copyright to 2003
//
// Revision 1.1.1.1  2003/11/14 23:16:33  klaus
// First put into repository
//
// Revision 1.6  2002/10/27 18:30:57  klaus
// simpel typing correction
//
// Revision 1.5  2002/10/27 18:29:56  klaus
// honor backward compatibilty with non-extended modifier addressing
//
// Revision 1.4  2002/10/27 17:02:30  klaus
// File addressing bug > 2 Gbtye circumvent
//
// Revision 1.3  2002/10/27 16:17:48  klaus
// Typing bug fixed caused at log addition
//
// Revision 1.2  2002/10/27 16:11:02  klaus
// Added CVS log into header
//
// what                                            who          when
// started                                         AR           03.07.1999
// first release 1.0                                                                     AR                           17.10.1999
// changed resource allocation caused by WIN2000   AR           08.06.2002
//

//-------------------------------------------------------------------------
// INCLUDES
//
#include <ntddk.h>
#include <devioctl.h>
#include <pcivme_drv.h>
#include <pcivme.h>
#include <pcivme_v.h>
#include <pcivme_io.h>
#include <pciif.h>
#include <pcivme_i.h>
#include <pcivme_fifo.h>

//------------------------------------------------------------------------
// DEFINES
//

// buffers usage must match the corresponding ioctl code!
#define SET_BUFFERS_METHOD_OUT_DIRECT \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
        pOutputBuffer = ((void *)(MmGetSystemAddressForMdl(Irp->MdlAddress)));\
}


#define SET_BUFFERS_METHOD_IN_DIRECT \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = ((void *)(MmGetSystemAddressForMdl(Irp->MdlAddress)));\
        pOutputBuffer = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
}


#define SET_BUFFERS_METHOD_BUFFERED \
{\
        InputLength   = IrpStack->Parameters.DeviceIoControl.InputBufferLength;\
        OutputLength  = IrpStack->Parameters.DeviceIoControl.OutputBufferLength;\
        pInputBuffer  = pOutputBuffer = ((void *)(Irp->AssociatedIrp.SystemBuffer));\
}


#define COMPLETE_REQUEST \
{\
        if (Status != STATUS_PENDING)\
        {\
                Irp->IoStatus.Status      = Status; \
                Irp->IoStatus.Information = irp_info; \
                IoCompleteRequest(Irp,IO_NO_INCREMENT); \
        }\
}


// compatibilty issues to WIN95 driver calls
#ifndef WORD
#define WORD USHORT
#endif

#ifndef DWORD
#define DWORD ULONG
#endif

#ifndef BYTE
#define BYTE UCHAR
#endif

#ifndef BOOL
#define BOOL BOOLEAN
#endif

#define MODIFIER_MASK     0x3F // mask for address modifier

//-------------------------------------------------------------------------
// GLOBALS
//
const PCIVME_INIT_ELEMENT init_element[] =
         {{LCR,  WORD_ACCESS, 0x4c,       DISABLE_PCIADA_IRQS}, // disable interrupts
          {LCR,  WORD_ACCESS, 0x50,       RELEASE_VMEMM},       // enable interface

          {VIC,  BYTE_ACCESS, (WORD)0x03, 0xf8+1},    // VIICR

          {VIC,  BYTE_ACCESS, (WORD)0x07, 0x78+1},    // VICR1
          {VIC,  BYTE_ACCESS, (WORD)0x0b, 0x78+2},
          {VIC,  BYTE_ACCESS, (WORD)0x0f, 0x78+3},
          {VIC,  BYTE_ACCESS, (WORD)0x13, 0x78+4},
          {VIC,  BYTE_ACCESS, (WORD)0x17, 0x78+5},
          {VIC,  BYTE_ACCESS, (WORD)0x1b, 0x78+6},
          {VIC,  BYTE_ACCESS, (WORD)0x1f, 0x78+7},    // VICR7

          {VIC,  BYTE_ACCESS, (WORD)0x23, 0xf8+0},    // DSICR

          {VIC,  BYTE_ACCESS, (WORD)0x27, 0xf8+1},    // LICR1
          {VIC,  BYTE_ACCESS, (WORD)0x2b, 0xf8+2},
          {VIC,  BYTE_ACCESS, (WORD)0x2f, 0xf8+3},
          {VIC,  BYTE_ACCESS, (WORD)0x33, 0xf8+4},
          {VIC,  BYTE_ACCESS, (WORD)0x37, 0xf8+5},
          {VIC,  BYTE_ACCESS, (WORD)0x3b, 0x38+6},
          {VIC,  BYTE_ACCESS, (WORD)0x3f, 0x38+7},    // LICR7

          {VIC,  BYTE_ACCESS, (WORD)0x43, 0xf8+2},    // ICGS
          {VIC,  BYTE_ACCESS, (WORD)0x47, 0xf8+3},    // ICMS

          {VIC,  BYTE_ACCESS, (WORD)0x4b, 0xf8+6},    // EGICR

          {VIC,  BYTE_ACCESS, (WORD)0x4f, 0x08},      // ICGS-IVBR (!)
          {VIC,  BYTE_ACCESS, (WORD)0x53, 0x0c},      // ICMS-IVBR (!)

          {VIC,  BYTE_ACCESS, (WORD)0x57, 0x00},      // LIVBR (!)

          {VIC,  BYTE_ACCESS, (WORD)0x5b, 0x10},      // EGIVBR (!)

          {VIC,  BYTE_ACCESS, (WORD)0x5f, 0x00},      // ICSR

          {VIC,  BYTE_ACCESS, (WORD)0x63, 0x00},      // ICR0
          {VIC,  BYTE_ACCESS, (WORD)0x67, 0x00},
          {VIC,  BYTE_ACCESS, (WORD)0x6b, 0x00},
          {VIC,  BYTE_ACCESS, (WORD)0x6f, 0x00},
          {VIC,  BYTE_ACCESS, (WORD)0x73, 0x00},      // ICR4

          {VIC,  BYTE_ACCESS, (WORD)0x83, 0xfe},      // VIRSR

          {VIC,  BYTE_ACCESS, (WORD)0x87, 0x0f},      // VIVR1
          {VIC,  BYTE_ACCESS, (WORD)0x8b, 0x0f},
          {VIC,  BYTE_ACCESS, (WORD)0x8f, 0x0f},
          {VIC,  BYTE_ACCESS, (WORD)0x93, 0x0f},
          {VIC,  BYTE_ACCESS, (WORD)0x97, 0x0f},
          {VIC,  BYTE_ACCESS, (WORD)0x9b, 0x0f},
          {VIC,  BYTE_ACCESS, (WORD)0x9f, 0x0f},      // VIVR7

          {VIC,  BYTE_ACCESS, (WORD)0xa3, 0x3c},      // TTR

          {VIC,  BYTE_ACCESS, (WORD)0xb3, 0x40},      // ARCR
          {VIC,  BYTE_ACCESS, (WORD)0xb7, 0x29},      // AMSR
          {VIC,  BYTE_ACCESS, (WORD)0xd3, 0x00},      // RCR

          {IFR,  LONG_ACCESS, (WORD)ADRHL, 0xF0F0F0F0},  // ADR-H, ADR-L
          {IFR,  WORD_ACCESS, (WORD)CSR  , 0x0000},      // Contr-Reg

          {VIC,  BYTE_ACCESS, (WORD)0x7f, 0x80},         // ICR7

          {LCR,  WORD_ACCESS, 0x4c, DISABLE_PCIADA_IRQS},// disable interrupts
         
          {STOP, WORD_ACCESS, 0,     0}};

const PCIVME_INIT_ELEMENT deinit_element_pre[] =
         {{VIC,  BYTE_ACCESS, (WORD)0x7f, 0x00},         // ICR7 - sysfail
          {LCR,  WORD_ACCESS, 0x4c, DISABLE_PCIADA_IRQS},// disable interrupts
          {STOP, WORD_ACCESS, 0,    0}};

const PCIVME_INIT_ELEMENT deinit_element_post[] =
         {{LCR,  WORD_ACCESS, 0x50, INHIBIT_VMEMM},      // disable interface
          {STOP, WORD_ACCESS, 0,    0}};


//--------------------------------------------------------------------------
// LOCAL FUNCTIONS
//

//--------------------------------------------------------------------------
// fast read or write functions - portable -
static void readByte(void *to, ULONG dwLength, void *from)
{
        READ_REGISTER_BUFFER_UCHAR((PUCHAR)from, (PUCHAR)to, dwLength);
}

static void readWord(void *to, ULONG dwLength, void *from)
{
        dwLength >>= 1;
        READ_REGISTER_BUFFER_USHORT((PUSHORT)from, (PUSHORT)to, dwLength);
}

static void readLong(void *to, ULONG dwLength, void *from)
{
        dwLength >>= 2;
        READ_REGISTER_BUFFER_ULONG((PULONG)from, (PULONG)to, dwLength);
}

static void writeByte(void *to, ULONG dwLength, void *from)
{
        WRITE_REGISTER_BUFFER_UCHAR((PUCHAR)to, (PUCHAR)from, dwLength);
}

static void writeWord(void *to, ULONG dwLength, void *from)
{
        dwLength >>= 1;
        WRITE_REGISTER_BUFFER_USHORT((PUSHORT)to, (PUSHORT)from, dwLength);
}

static void writeLong(void *to, ULONG dwLength, void *from)
{
        dwLength >>= 2;
        WRITE_REGISTER_BUFFER_ULONG((PULONG)to, (PULONG)from, dwLength);
}

//------------------------------------------------------------------------
// insert the irq queue into the linked list of queues
static NTSTATUS insertQueueInList(PFILE_OBJ pFile_obj, PCIADA *pciada)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        FIFO_LIST   *next;
        KIRQL           OldIrql;

        KdPrint(("insertQueueInList()\n"));

        if (pFile_obj->bQueueIrq)  // still enabled and in list
                return Status;

        // allocate memory to hold the list and its container
        next = (FIFO_LIST *)ExAllocatePool(NonPagedPool, sizeof(FIFO_LIST));
        if(next == (FIFO_LIST *)NULL)
                return STATUS_INSUFFICIENT_RESOURCES;
        else
        {
                // fill contents in entry
                next->pFile_obj                 = pFile_obj;
                next->pIrqListHandle    = pFile_obj->pIrqListHandle;

                // insert the entry in the list
                KeAcquireSpinLock(&pciada->IrqListLock, &OldIrql);
                KdPrint(("InsertHeadList(0x%08x, 0x%08x)\n", &pciada->IrqListList, &next->entry));
                InsertHeadList(&pciada->IrqListList, &next->entry);
                KeReleaseSpinLock(&pciada->IrqListLock, OldIrql);

                // show and mark it
                pFile_obj->bQueueIrq = TRUE;
        }

        return Status;
}

//------------------------------------------------------------------------
// remove the irq queue from the linked list of queues
NTSTATUS removeQueueFromList(PFILE_OBJ pFile_obj, PCIADA *pciada)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        FIFO_LIST   *next;
        PLIST_ENTRY pList;
        KIRQL           OldIrql;

        KdPrint(("removeQueueFromList(0x%08x, 0x%08x)\n", pFile_obj, pciada));

        pList = &pciada->IrqListList;  

        if ((pFile_obj == (FILE_OBJ *)NULL) || (pciada == (PCIADA *)NULL)) return Status;

        // search for coincidence of pFile_obj in the list
        KeAcquireSpinLock(&pciada->IrqListLock, &OldIrql);
        while (pList->Flink != &pciada->IrqListList)
        {
                pList = pList->Flink;
                next = CONTAINING_RECORD(pList, FIFO_LIST, entry);
                if (next->pFile_obj == pFile_obj)   // found
                {
                        KdPrint(("RemoveEntryList(0%08x)\n", pList));
                        RemoveEntryList(pList);
                        ExFreePool((PVOID)next);
                        break;
                }
        }
        KeReleaseSpinLock(&pciada->IrqListLock, OldIrql);

        // in every case switch it off (again)
        pFile_obj->bQueueIrq = FALSE;

        KdPrint(("removeQueueFromList OK\n"));
        return Status;
}

//--------------------------------------------------------------------------
// parsing of user supplied input for validy
static BOOLEAN check_command(const PCIVME_INIT_ELEMENT *psInitElement)
{
  USHORT range;
  USHORT access_size;
 
  switch (psInitElement->range)
  {
    case LCR: range = 0x54;     break;
    case IFR: range = 0x0c;     break;
    case VIC: range = 0xe4;
              if ((psInitElement->offset & 3) != 3)
                return FALSE;
              break;
    default:  range = 0;        break;
  }

  switch (psInitElement->type)
  {
    case LONG_ACCESS: if (psInitElement->offset & 3)
                        return FALSE;
                      access_size = sizeof(ULONG);
                      break;
    case WORD_ACCESS: if (psInitElement->offset & 1)
                        return FALSE;
                      access_size = sizeof( USHORT);
                      break;
    case BYTE_ACCESS: access_size = sizeof( UCHAR); break;
    default         : access_size = 0xFFFF;        break;
  }

  if ((psInitElement->offset + access_size) > range)
    return FALSE;       // ignore it
   
  return TRUE;
}

//------------------------------------------------------------------------
// iterate through all commands
static VOID CmdMachine(const PCIVME_INIT_ELEMENT *psInitElement,
                                                 PVOID pvLcr, PVOID pvIfr)
{
  PVOID adr;
 
  // standard initialisierungen  
  while (psInitElement->range != STOP)
  {
    /*
        KdPrint(("CmdMachine: %d %d 0x%02x 0x%02x\n",
                psInitElement->range,  psInitElement->type,
                psInitElement->offset, psInitElement->value));
    */


    if (check_command(psInitElement))
    {
      switch (psInitElement->range)
      {
        case LCR: adr = pvLcr; break;
        case VIC: adr = (PVOID)((ULONG)pvIfr + (USHORT)VICBASE); break;
        case IFR:
        default:  adr = pvIfr; break;  
      }

      switch (psInitElement->type)
      {
        case LONG_ACCESS:
                  WRITE_REGISTER_ULONG((ULONG *)((UCHAR *)adr + psInitElement->offset),  
                                                                     psInitElement->value);
          break;
        case WORD_ACCESS:
                  WRITE_REGISTER_USHORT((USHORT *)((UCHAR *)adr + psInitElement->offset),
                                                             (USHORT)psInitElement->value);
          break;
        case BYTE_ACCESS:
        default:
                  WRITE_REGISTER_UCHAR((UCHAR *)((UCHAR *)adr + psInitElement->offset),  
                                                              (UCHAR)psInitElement->value);
          break;
      }
    }
    psInitElement++;
  }
}

//------------------------------------------------------------------------
// init the interface with build in and user supplied constants
static BOOLEAN InitInterface(const PCIVME_INIT_COMMAND *psInitCmd,
                                                    PVOID pvLcr, PVOID pvIfr)
{
  PCIVME_INIT_ELEMENT *psVie;
 
  if ((psInitCmd == (PCIVME_INIT_COMMAND *)BOGUSADDRESS) ||
      (psInitCmd == (PCIVME_INIT_COMMAND *)NULL)         ||
          (pvLcr         == (PVOID)BOGUSADDRESS)                 ||
      (pvLcr     == (PVOID)NULL)                         ||
          (pvIfr         == (PVOID)BOGUSADDRESS)                 ||
          (pvIfr     == (PVOID)NULL))
    return FALSE;

  psVie = (PCIVME_INIT_ELEMENT *)psInitCmd->sVie;
  CmdMachine(init_element , pvLcr, pvIfr);    // standard initialisierungen  
  CmdMachine(psVie,         pvLcr, pvIfr);    // benutzer initialisierungen
 
  return TRUE;
}

// deinit the interface with user supplied and build in constants
static BOOLEAN DeInitInterface(const PCIVME_INIT_COMMAND *psDeInitCmd,
                                                   PVOID pvLcr, PVOID pvIfr)
{
  PCIVME_INIT_ELEMENT *psVie;

  if ((psDeInitCmd == (PCIVME_INIT_COMMAND *)BOGUSADDRESS) ||
      (psDeInitCmd == (PCIVME_INIT_COMMAND *)NULL)         ||
          (pvLcr           == (PVOID)BOGUSADDRESS)                 ||
      (pvLcr       == (PVOID)NULL)                         ||
          (pvIfr           == (PVOID)BOGUSADDRESS)                 ||
          (pvIfr       == (PVOID)NULL))
    return FALSE;

  psVie = (PCIVME_INIT_ELEMENT *)psDeInitCmd->sVie;
  CmdMachine(deinit_element_pre,   pvLcr, pvIfr); // standard de-initialisierungen  
  CmdMachine(psVie,                pvLcr, pvIfr); // benutzer de-initialisierungen
  CmdMachine(deinit_element_post , pvLcr, pvIfr); // disable interface
 
  return TRUE;
}

//------------------------------------------------------------------------
// a inserter into a user managed queue of IRPs
//
void InsertIRPtoQueue(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        DEVICE_EXT *pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        KIRQL           oldIrql;

    KdPrint(("InsertIRPtoQueue(0x%08x)\n", Irp));

        KeAcquireSpinLock(&pDevExt->IRPLock, &oldIrql);
        InsertHeadList(&pDevExt->IRPList, &Irp->Tail.Overlay.ListEntry);
        KeReleaseSpinLock(&pDevExt->IRPLock, oldIrql);
}

//------------------------------------------------------------------------
// a remover out of a user managed queue of IRPs
//
PIRP RemoveIRPfromQueue(PDEVICE_OBJECT device_Obj, FILE_OBJ *pFile_obj)
{
        DEVICE_EXT      *pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        KIRQL           oldIrql;
        register PLIST_ENTRY pList = &pDevExt->IRPList;
        PIRP        Irp;
        PIRP        pIrp = (PIRP)NULL;
        FILE_OBJ        *file_obj;

    KdPrint(("RemoveIRPfromQueue()\n"));

        KeAcquireSpinLock(&pDevExt->IRPLock, &oldIrql);
 
        while (pList->Flink != &pDevExt->IRPList)  // until the end is reached
        {
                pList = pList->Flink;
                Irp  = CONTAINING_RECORD(pList, IRP, Tail.Overlay.ListEntry);
                file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

                KdPrint(("pList 0x%08x, pList->Flink 0x%08x, Irp 0x%08x, file_obj 0x%08x\n", pList, pList->Flink, Irp, file_obj));

                if ((file_obj == pFile_obj) && (pFile_obj != (FILE_OBJ *)NULL))
                {
                        RemoveEntryList(pList);
                        pIrp = Irp;
                        break;
                }
        }

        KeReleaseSpinLock(&pDevExt->IRPLock, oldIrql);

    KdPrint(("return RemoveIRPfromQueue(0x%08x)\n", pIrp));

        return pIrp;
}

//------------------------------------------------------------------------
// a remover out of a user managed queue of IRPs
//
PIRP RemoveIRPfromQueueByIrp(PDEVICE_OBJECT device_Obj, PIRP pIrpIn)
{
        DEVICE_EXT      *pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        KIRQL           oldIrql;
        register PLIST_ENTRY pList = &pDevExt->IRPList;
        PIRP        Irp;
        PIRP        pIrp = (PIRP)NULL;

    KdPrint(("RemoveIRPfromQueueByIrp()\n"));

        KeAcquireSpinLock(&pDevExt->IRPLock, &oldIrql);

        while (pList->Flink != &pDevExt->IRPList)  // until the end is reached
        {
                pList = pList->Flink;
                Irp  = CONTAINING_RECORD(pList, IRP, Tail.Overlay.ListEntry);

                KdPrint(("pList 0x%08x, pList->Flink 0x%08x, Irp 0x%08x\n", pList, pList->Flink, Irp));

                if (pIrpIn == Irp)
                {
                        RemoveEntryList(pList);
                        pIrp = Irp;
                        break;
                }
        }

        KeReleaseSpinLock(&pDevExt->IRPLock, oldIrql);

    KdPrint(("return RemoveIRPfromQueueByIrp(0x%08x)\n", pIrp));

        return pIrp;
}

//------------------------------------------------------------------------
// the default cancel routine for an queued Irp
//
void CancelRequest(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        PIRP pIrpCancel = RemoveIRPfromQueueByIrp(device_Obj, Irp);

        if (pIrpCancel == (PIRP)NULL)
        {
                IoReleaseCancelSpinLock(Irp->CancelIrql);
                KdPrint(("Nothing to do: CancelRequest(0x%08x)\n", Irp));
                return;
        }
        else
        {
                IoReleaseCancelSpinLock(Irp->CancelIrql);

                KdPrint(("Done: CancelRequest(0x%08x)\n", Irp));

                Irp->IoStatus.Status = STATUS_CANCELLED;
                Irp->IoStatus.Information = 0;
 
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
        }
}
 

//------------------------------------------------------------------------
// the custom deffered routine to finish blocking io on read_vector
void fMyDefferedRoutine(PKDPC Dpc, PVOID pvDevice_object,
                                                                         PVOID pvPciada, PVOID pvVector)
{
        NTSTATUS Status = STATUS_SUCCESS;
        ULONG irp_info  = sizeof(PCIVME_VECTOR_RESPONSE);
    PIRP  Irp = (PIRP)NULL;
        PDEVICE_OBJECT device_Obj = (PDEVICE_OBJECT)pvDevice_object;
        PCIADA *pciada = (PCIADA *)pvPciada;
        FIFO_LIST               *next;
        KIRQL                   oldIrqlCancel;
        KIRQL                   oldIrqlList;
        register PLIST_ENTRY pList = &pciada->IrqListList;
        UNREFERENCED_PARAMETER(pvVector);
        UNREFERENCED_PARAMETER(Dpc);
    KdPrint(("fMyDefferedRoutine()\n"));

        // beware off damage due to intercept with cancel of thread
    IoAcquireCancelSpinLock(&oldIrqlCancel);
        KeAcquireSpinLock(&pciada->IrqListLock, &oldIrqlList);
 
        while (pList->Flink != &pciada->IrqListList)  // until the end is reached
        {
                pList = pList->Flink;
                KeReleaseSpinLock(&pciada->IrqListLock, oldIrqlList); // shorten block
 
                next = CONTAINING_RECORD(pList, FIFO_LIST, entry);

                // get my associated packet
                Irp = RemoveIRPfromQueue(device_Obj, next->pFile_obj);

                if (Irp != (PIRP)NULL)
                {
                        PCIVME_VECTOR_REQUEST  *pVectorRequest  = (PCIVME_VECTOR_REQUEST  *)(Irp->AssociatedIrp.SystemBuffer);
                        PCIVME_VECTOR_RESPONSE *pVectorResponse = (PCIVME_VECTOR_RESPONSE *)pVectorRequest;
                        USHORT wRequestCount = pVectorRequest->wRequestCount;
                        UCHAR  *pbVector     = &pVectorResponse->bStatusID;
                        FILE_OBJ *file_obj   = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
                        UCHAR  bNumberOfElements = (UCHAR) NumberOfElements(file_obj->pIrqListHandle);
 
                        // pull the vectors off the fifo
                        pVectorResponse->wCount = 0;
                        while ((bNumberOfElements) && (wRequestCount--))
                        {
                                bNumberOfElements   = (UCHAR) PullElement(file_obj->pIrqListHandle,
                                                                        (void *)pbVector);
                                pbVector++;
                                pVectorResponse->wCount++;
                        }
                        pVectorResponse->wPendingCount = bNumberOfElements;
                        pVectorResponse->bOverflow     = CheckAndClearOverflow(file_obj->pIrqListHandle);
                        irp_info = sizeof(PCIVME_VECTOR_RESPONSE) +
                                     sizeof(UCHAR) * (pVectorResponse->wCount - 1);

                        // release the cancel routine from this Irp
                        IoSetCancelRoutine(Irp, NULL);

                        COMPLETE_REQUEST;
                }
       
                KeAcquireSpinLock(&pciada->IrqListLock, &oldIrqlList);
        }

        // release the spin locks
        KeReleaseSpinLock(&pciada->IrqListLock, oldIrqlList);
        IoReleaseCancelSpinLock(oldIrqlCancel);
}

//------------------------------------------------------------------------
// all functions called from ioctl jump table
//

//------------------------------------------------------------------------
// initialize all hardware associated to a given wModuleNumber
static NTSTATUS ioctl_init_hardware(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        DEVICE_EXT      *pDevExt;
        PCIVME_INIT_COMMAND *pInitCommand;
        PCIADA      *pciada;
        FILE_OBJ        *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

        // do what must be here in between --- start ---
        pInitCommand = (PCIVME_INIT_COMMAND *)pInputBuffer;

    KdPrint(("ioctl_init_hardware(%d)\n", file_obj->uwAssociatedVMEMM));

        pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

        if (pciada != NULL)
        {
                KIRQL oldIrql;

                if (pciada->dwLinkCount == 1)
                {
                        // lock other users out
                        KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                        if (InitInterface(pInitCommand, pciada->pvVirtLcr, pciada->pvVirtIfr))
                        {
                                // fill cache for page and modifier
                                pciada->dwVMEPage = READ_REGISTER_ULONG(pciada->pdwVMEAdr)  & ~VME_ADR_MASK;
                                pciada->bModifier = READ_REGISTER_UCHAR(pciada->pbModifier) & MODIFIER_MASK;
                        }
                        else
                                Status = STATUS_UNSUCCESSFUL;

                        // release the lock
                        KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
                }
                else
                        Status = STATUS_UNSUCCESSFUL;
        }
        else
                Status = STATUS_UNSUCCESSFUL;

        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_init_hardware(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// De-initialise all hardware associated to a given wModuleNumber
static NTSTATUS ioctl_deinit_hardware(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        DEVICE_EXT      *pDevExt;
        PCIVME_INIT_COMMAND *pInitCommand;
        PCIADA      *pciada;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
    KdPrint(("ioctl_deinit_hardware()\n"));

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

        // do what must be hier in between --- start ---
        pInitCommand = (PCIVME_INIT_COMMAND *)pInputBuffer;

    KdPrint(("ioctl_deinit_hardware(%d)\n", file_obj->uwAssociatedVMEMM));

        pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

        if (pciada != NULL)
        {
                KIRQL oldIrql;

                if (pciada->dwLinkCount == 1)
                {
                        // lock other users out
                        KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                        if (DeInitInterface(pInitCommand, pciada->pvVirtLcr, pciada->pvVirtIfr))
                                globalInterruptDisable(pciada);
                        else
                                Status = STATUS_UNSUCCESSFUL;

                        // everyone likes to have PCIVME
                        KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
                }
                else
                        Status = STATUS_UNSUCCESSFUL;
        }
        else
                Status = STATUS_UNSUCCESSFUL;
        // do what must be hier in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_deinit_hardware(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// a dummy entry because of compatibiltiy (near) WIN95 driver
static NTSTATUS ioctl_dummy(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIVME_INIT_COMMAND *pInitCommand;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        SET_BUFFERS_METHOD_BUFFERED;
        UNREFERENCED_PARAMETER(device_Obj);
#ifndef _DEBUG
        UNREFERENCED_PARAMETER(file_obj);
#endif
        pInitCommand = (PCIVME_INIT_COMMAND *)pInputBuffer;

    KdPrint(("ioctl_dummy(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
       
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_dummy(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// requests fixed unchangeable information - not compatible to WIN95 driver
static NTSTATUS ioctl_get_static_status(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_STATIC_STATUS);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        PCIVME_STATIC_STATUS *pStaticStatus;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_get_static_status(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        if (OutputLength >= sizeof(PCIVME_STATIC_STATUS))
        {
                pStaticStatus = (PCIVME_STATIC_STATUS *)pOutputBuffer;

                pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

                pStaticStatus->dwInterface              = file_obj->uwAssociatedVMEMM;  

                pStaticStatus->wNumMemWindows   = 3;      
                pStaticStatus->wNumIOPorts              = 2;
                pStaticStatus->wNumIRQs                 = 1;
                pStaticStatus->wNumDMAs                 = 0;
     
                pStaticStatus->dwLinkCount              = pciada->dwLinkCount;

                pStaticStatus->wModuleType              = pciada->wModuleType;
                pStaticStatus->wFPGAVersion             = pciada->wFPGAVersion;
                pStaticStatus->wModuleNumber    = pciada->wModuleNumber;
                pStaticStatus->wWordMode                = pciada->bWordMode;

                pStaticStatus->wSysControl              = pciada->bSysControl;
                pStaticStatus->wConnected               = pciada->bConnected;
 
                pStaticStatus->pvLcr                    = pciada->pvPhysLcr;
                pStaticStatus->pvIfr                    = pciada->pvPhysIfr;
               
                pStaticStatus->dwDriverVersion  = DRIVER_VERSION;
                pStaticStatus->dwDriverVariant  = DRIVER_VARIANT;
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_get_static_status(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// requests changeable status
static NTSTATUS ioctl_get_dynamic_status(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_DYNAMIC_STATUS);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        PCIVME_DYNAMIC_STATUS *pDynamicStatus;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_get_dynamic_status(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_get_static_status(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        if (OutputLength >= sizeof(PCIVME_DYNAMIC_STATUS))
        {
                USHORT temp;

                pDynamicStatus = (PCIVME_DYNAMIC_STATUS *)pOutputBuffer;

                pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

                pDynamicStatus->dwInterface     = file_obj->uwAssociatedVMEMM;
 
                temp = READ_REGISTER_USHORT(pciada->pwCntrl);
                pDynamicStatus->wVMEMM_enable           = ((temp & 0x0180) == 0x0180) ? 1 : 0;
                pDynamicStatus->wVMEMM_connected        = ((temp & 0x0c00) == 0x0800) ? 1 : 0;
                temp = READ_REGISTER_USHORT(pciada->pwIntCSR);
                pDynamicStatus->wPCIADAIrq                      = (temp & 0x0004) ? 1 : 0;
                pDynamicStatus->wVMEMMIrq                       = (temp & 0x0020) ? 1 : 0;
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;

        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_get_dynamic_status(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// get the next vector out of the vector queue
static NTSTATUS ioctl_read_vector(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_VECTOR_RESPONSE);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIVME_VECTOR_RESPONSE *pVectorResponse;
        PCIVME_VECTOR_REQUEST  *pVectorRequest;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
    KIRQL       oldIrql;
        BOOLEAN     bPoll;
        USHORT      wRequestCount;

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_read_vector(%d)\n", file_obj->uwAssociatedVMEMM));

        pVectorRequest  = (PCIVME_VECTOR_REQUEST  *)pInputBuffer;
        pVectorResponse = (PCIVME_VECTOR_RESPONSE *)pOutputBuffer;

        // check the available room for vectors and correct if too less
        if (OutputLength <
                   (sizeof(PCIVME_VECTOR_RESPONSE) +
                       (pVectorRequest->wRequestCount - 1) * sizeof(UCHAR)))
                pVectorRequest->wRequestCount =
                    (USHORT)OutputLength - sizeof(PCIVME_VECTOR_RESPONSE) + sizeof(UCHAR);
 
        // empty the inputbuffer as early as possible
        wRequestCount = pVectorRequest->wRequestCount;
        bPoll             = pVectorRequest->bPoll;

        // do what must be here in between -----------
    if (OutputLength >= sizeof(PCIVME_VECTOR_RESPONSE)) // at least room for one
        {
                UCHAR  bNumberOfElements;
                UCHAR  *pbVector = &pVectorResponse->bStatusID;

                pVectorResponse->dwInterface = file_obj->uwAssociatedVMEMM;                    

                bNumberOfElements = (UCHAR) NumberOfElements(file_obj->pIrqListHandle);

                if ((bNumberOfElements) || (bPoll))
                {
                        KdPrint(("Direct return (%d)\n", bNumberOfElements));

                        pVectorResponse->wCount = 0;
                        while ((bNumberOfElements) && (wRequestCount--))
                        {
                                bNumberOfElements   = (UCHAR) PullElement(file_obj->pIrqListHandle,
                                                                        (void *)pbVector);
                                pbVector++;
                                pVectorResponse->wCount++;
                        }
                        pVectorResponse->wPendingCount = bNumberOfElements;
                        pVectorResponse->bOverflow     = CheckAndClearOverflow(file_obj->pIrqListHandle);
                        irp_info = sizeof(PCIVME_VECTOR_RESPONSE) +
                                     sizeof(UCHAR) * (pVectorResponse->wCount - 1);
                }
                else                    // go in wait queue for an irq
                {
                        IoAcquireCancelSpinLock(&oldIrql);

                        if (Irp->Cancel)    // cancel while doing
                        {
                                KdPrint(("Canceld return (%d)\n", bNumberOfElements));
                                Status = STATUS_CANCELLED;
                        }
                        else
                        {
                                KdPrint(("Blocking return (%d)\n", bNumberOfElements));

                                InsertIRPtoQueue(device_Obj, Irp);

                                Status = STATUS_PENDING;

                                // mark irp as pending and return
                                IoMarkIrpPending(Irp);
                                IoSetCancelRoutine(Irp, CancelRequest);
                        } // if (Irp->Cancel) ...
                       
                        IoReleaseCancelSpinLock(oldIrql);
                }
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;
       
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_read_vector(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// control or read the VIC68A on the VMEMM
static NTSTATUS ioctl_access_VIC68A(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_VIC68A_ACTION);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        PCIVME_VIC68A_ACTION *pVIC68A_action_in;
        PCIVME_VIC68A_ACTION *pVIC68A_action_out;
        FILE_OBJ        *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_access_VIC68A(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        pVIC68A_action_in       = (PCIVME_VIC68A_ACTION *)pInputBuffer;
        pVIC68A_action_out      = (PCIVME_VIC68A_ACTION *)pOutputBuffer;

        if ((pVIC68A_action_in->wRegisterAddress <= (USHORT)SRR)    &&
                        ((pVIC68A_action_in->wRegisterAddress & 0x03) == 3) &&
                                (OutputLength >= sizeof(PCIVME_VIC68A_ACTION)))
        {
                PUCHAR  pbAddress;
                UCHAR   bByte=0;
                KIRQL   oldIrql;

                pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

                pVIC68A_action_out      = pVIC68A_action_in;  // copy it  
       
                pbAddress = (PUCHAR)((ULONG)(pciada->pvVirtIfr) + VICBASE + pVIC68A_action_in->wRegisterAddress);
 
                // lock other users out
                KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                switch(pVIC68A_action_in->wAccessMode)
                {
                        case VIC68A_WRITE:      WRITE_REGISTER_UCHAR(pbAddress, pVIC68A_action_in->bContent);
                                                                bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                break;
                        case VIC68A_WRITE_ONLY:
                                                                WRITE_REGISTER_UCHAR(pbAddress, pVIC68A_action_in->bContent);
                                                                break;
                        case VIC68A_OR:         bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                bByte     |= pVIC68A_action_in->bContent;
                                                                WRITE_REGISTER_UCHAR(pbAddress, bByte);
                                                                bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                break;
                        case VIC68A_AND:        bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                bByte     &= pVIC68A_action_in->bContent;
                                                                WRITE_REGISTER_UCHAR(pbAddress, bByte);
                                                                bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                break;
                        default:                        Status = STATUS_ILLEGAL_INSTRUCTION;
                        case VIC68A_READ:       bByte      = READ_REGISTER_UCHAR(pbAddress);
                                                                break;
                }
                // free lock
                KeReleaseSpinLock(&pciada->AccessLock, oldIrql);

                pVIC68A_action_out->bContent = bByte;
        }
        else
                Status = STATUS_ILLEGAL_INSTRUCTION;
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_access_VIC68A(), Status = 0x%08x\n", Status));

        return Status;
}


//------------------------------------------------------------------------
// if the interrupt is disabled for a blocking path, cancel the block
static void ReleaseBlockingIrp(PDEVICE_OBJECT device_Obj, PCIADA *pciada, PFILE_OBJ pFile_obj)
{
        NTSTATUS Status = STATUS_CANCELLED;
        PIRP                    Irp;
        KIRQL                   oldIrqlCancel;
        KIRQL                   oldIrqlList;

        // beware off damage due to intercept with cancel of thread
        IoAcquireCancelSpinLock(&oldIrqlCancel);
        KeAcquireSpinLock(&pciada->IrqListLock, &oldIrqlList);

        // get my associated packet
        Irp = RemoveIRPfromQueue(device_Obj, pFile_obj);

        if (Irp != (PIRP)NULL)
        {
                PCIVME_VECTOR_REQUEST  *pVectorRequest  = (PCIVME_VECTOR_REQUEST  *)(Irp->AssociatedIrp.SystemBuffer);
                PCIVME_VECTOR_RESPONSE *pVectorResponse = (PCIVME_VECTOR_RESPONSE *)pVectorRequest;
                ULONG irp_info  = sizeof(PCIVME_VECTOR_RESPONSE);

                // pull the vectors off the fifo
                pVectorResponse->wCount            = 0;
                pVectorResponse->wPendingCount = 0;
                pVectorResponse->bOverflow     = FALSE;
                irp_info = sizeof(PCIVME_VECTOR_RESPONSE);

                // release the cancel routine from this Irp
                IoSetCancelRoutine(Irp, NULL);

                COMPLETE_REQUEST;
        }

        // release the spin locks
        KeReleaseSpinLock(&pciada->IrqListLock, oldIrqlList);
        IoReleaseCancelSpinLock(oldIrqlCancel);
}


//------------------------------------------------------------------------
// switch the filling of the interrupt vector queue on or off, check the queue
static NTSTATUS ioctl_control_interrupts(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_IRQ_CONTROL);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        PCIVME_IRQ_CONTROL *pIrqControlIn;
        PCIVME_IRQ_CONTROL *pIrqControlOut;
        PFILE_OBJ pFile_obj = (PFILE_OBJ)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_control_interrupts(%d)\n", pFile_obj->uwAssociatedVMEMM));

        pIrqControlIn  = (PCIVME_IRQ_CONTROL *)pInputBuffer;
        pIrqControlOut = (PCIVME_IRQ_CONTROL *)pOutputBuffer;

        pciada = pDevExt->vmemm[pFile_obj->uwAssociatedVMEMM];

        // do what must be here in between -----------
        if (pIrqControlIn->wEnable)
        {
                Status = insertQueueInList(pFile_obj, pciada);
                if (!pciada->nInterruptHandlers)
                {
                        KdPrint(("Interrupts enabled.\n"));

                        globalInterruptEnable(pciada);
                        pciada->nInterruptHandlers++;
                }
        }
        else
        {
                if (pciada->nInterruptHandlers <= 1)
                {
                        KdPrint(("Interrupts disabled.\n"));

                        globalInterruptDisable(pciada);
                        pciada->nInterruptHandlers = 0;
                }

                Status = removeQueueFromList(pFile_obj, pciada);
                ReleaseBlockingIrp(device_Obj, pciada, pFile_obj);
        }

        // give back if the user grants space
        if (OutputLength >= sizeof(PCIVME_IRQ_CONTROL))
        {
                pIrqControlOut->dwInterface = pFile_obj->uwAssociatedVMEMM;
                pIrqControlOut->wEnable     = pFile_obj->bQueueIrq;
        }
       
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_control_interrupts(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// generate a uninterruptible read-modify-write cycle
static NTSTATUS ioctl_TAS(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_TAS_STRUCT);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIADA      *pciada;
        DEVICE_EXT      *pDevExt;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        KIRQL           oldIrql;
        UCHAR           tempContent;
        PBOOLEAN        pbPrevBusError;
        PVOID       pvPartialAdr;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;
 
    KdPrint(("ioctl_TAS(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        if (OutputLength >= sizeof(PCIVME_TAS_STRUCT))
        {
                PCIVME_TAS_STRUCT *pTAS_struct_in;
                PCIVME_TAS_STRUCT *pTAS_struct_out;
                USHORT csr;                     // storage for old csr content
                ULONG  pageAddress; // intermediate for the page register content
                UCHAR  bAddressModifier;

                pTAS_struct_in  = (PCIVME_TAS_STRUCT *)pInputBuffer;
                pTAS_struct_out = (PCIVME_TAS_STRUCT *)pOutputBuffer;

                pTAS_struct_out = pTAS_struct_in;
                pTAS_struct_out->dwInterface = file_obj->uwAssociatedVMEMM;
                pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

                // take the file_obj associated modifier if greater than ...
                bAddressModifier = (pTAS_struct_in->wModifier > 63) ?
                        file_obj->bAddressModifier : (UCHAR)pTAS_struct_in->wModifier;
 
                // lock other users out
                KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                // check for modifier (and set if needed)
                if (pciada->bModifier != bAddressModifier)
                {
                        WRITE_REGISTER_UCHAR(pciada->pbModifier, bAddressModifier);
                        pciada->bModifier = bAddressModifier;
                }
               
                // check for page address (and set if needed)
                pageAddress = pTAS_struct_in->dwAddress & ~VME_ADR_MASK;
                if (pageAddress != pciada->dwVMEPage)
                {
                        WRITE_REGISTER_ULONG(pciada->pdwVMEAdr, pageAddress);
                        pciada->dwVMEPage = pageAddress;
                }
 
                // save VMEMM csr register and prepare for read modify write
                csr = READ_REGISTER_USHORT(pciada->pwCSR);
                WRITE_REGISTER_USHORT(pciada->pwCSR,  (USHORT)(csr | FLAG_RMC));

                // prepare the TAS
                tempContent = pTAS_struct_in->bContent; // in and out point to same buffer
                pvPartialAdr = (PVOID)((PUCHAR)pciada->pvVME + (pTAS_struct_in->dwAddress & VME_ADR_MASK));

                // get prepared for bus errors
                file_obj->bBusError = FALSE;
                pbPrevBusError = ExchangePointer(&pciada->pbBusError, &file_obj->bBusError);

                // do the TAS
                readByte(&pTAS_struct_out->bContent , 1, pvPartialAdr);
                writeByte(pvPartialAdr , 1, &tempContent);
                readByte(&tempContent , 1, pvPartialAdr);  // to overcome write on ..

                // restore csr
                WRITE_REGISTER_USHORT(pciada->pwCSR, csr);

                // release the lock
                KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
                ExchangePointer(&pciada->pbBusError, pbPrevBusError);
                if (file_obj->bBusError) Status = STATUS_ACCESS_VIOLATION;
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;

        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_TAS(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// make a VME reset
static NTSTATUS ioctl_reset(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = sizeof(PCIVME_RESET_RESULT);
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        DEVICE_EXT      *pDevExt;
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

        pDevExt = (DEVICE_EXT *)(device_Obj->DeviceExtension);

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_reset(%d)\n", file_obj->uwAssociatedVMEMM));

        // do what must be here in between -----------
        if (OutputLength >= sizeof(PCIVME_RESET_RESULT))
        {
                PCIVME_RESET_RESULT  *pResetResult  = (PCIVME_RESET_RESULT *)pOutputBuffer;
                PCIVME_RESET_COMMAND *pResetCommand = (PCIVME_RESET_COMMAND *)pInputBuffer;
                PCIADA                           *pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];
                USHORT wIRQStatus;
                USHORT wControl;
                UCHAR  *pbReset;
                KIRQL  oldIrql;

                // set default result return size and contents
                pResetResult->dwInterface       = file_obj->uwAssociatedVMEMM;
 
                if (pciada->dwLinkCount == 1)
                {
                        // lock other users out
                        KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                        // am I connected and switched on??
                        if ((READ_REGISTER_USHORT(pciada->pwCntrl) & 0x0980) == 0x0980)
                        {
                                // do command
                                switch (pResetCommand->wCommand)
                                {
                                        case POLL_RESET_CMD:  
                                                break;
                                        case VME_RESET_CMD:    
                                                WRITE_REGISTER_UCHAR(pciada->pbModifier, 0);
                                                pbReset = (UCHAR  *)((UCHAR *)pciada->pvVirtIfr +
                                                                                                (ULONG)VICBASE + (ULONG)SRR);
                                                WRITE_REGISTER_UCHAR(pbReset, 0xf0);  // make VME reset                        
                                                break;
                                        case LOCAL_RESET_CMD:  
                                                WRITE_REGISTER_UCHAR(pciada->pbModifier, 0);
                                                WRITE_REGISTER_USHORT(pciada->pwIRQStat, LOCAL_RESET);
                                                break;
                                        case GLOBAL_RESET_CMD:
                                                WRITE_REGISTER_UCHAR(pciada->pbModifier, 0);
                                                WRITE_REGISTER_USHORT(pciada->pwIRQStat, GLOBAL_RESET);
                                                break;

                                        default: Status = STATUS_ILLEGAL_INSTRUCTION;
                                }

                                // save IRQ status of PCIADA and switch off PCIADA interrupts
                                wIRQStatus = READ_REGISTER_USHORT(pciada->pwIntCSR);
                                WRITE_REGISTER_USHORT(pciada->pwIntCSR, (USHORT)(wIRQStatus & ~0x0040));
 
                                // always poll reset status - access will sometimes generate PCIADA #2 interrupt
                                pResetResult->wResult = READ_REGISTER_UCHAR(pciada->pbModifier);

                                // reset any pending PCIADA interrupt #2  
                                wControl   = READ_REGISTER_USHORT(pciada->pwCntrl);
                                WRITE_REGISTER_USHORT(pciada->pwCntrl, (USHORT)(wControl & ~0x0100));
                                WRITE_REGISTER_USHORT(pciada->pwCntrl, wControl);
     
                                // restore IRQStatus
                                WRITE_REGISTER_USHORT(pciada->pwIntCSR, wIRQStatus);

                        }
                        else
                                Status = STATUS_ALREADY_DISCONNECTED;

                        // get other users free entry
                        KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
                }
                else
                        Status = STATUS_UNSUCCESSFUL;
        }
        else
                Status = STATUS_BUFFER_TOO_SMALL;
        // do what must be here in between --- end ---

        COMPLETE_REQUEST;

    KdPrint(("ioctl_reset(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// set parameter for this path for future access to VME
static NTSTATUS ioctl_access_para(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack)
{
        NTSTATUS        Status    = STATUS_SUCCESS;
        ULONG           irp_info  = 0;
        PVOID           pInputBuffer,pOutputBuffer;
        ULONG           InputLength, OutputLength;
        PCIVME_ACCESS_COMMAND *pAccessPara;
        FILE_OBJ    *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(device_Obj->DeviceExtension);
        PCIADA      *pciada   = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];

        SET_BUFFERS_METHOD_BUFFERED;

    KdPrint(("ioctl_access_para(%d)\n", file_obj->uwAssociatedVMEMM));
   
        pAccessPara = (PCIVME_ACCESS_COMMAND *)pInputBuffer;

        // do here in between what has to be done -----------------
        file_obj->bAddressModifier  = pAccessPara->bAddressModifier & MODIFIER_MASK;
        file_obj->bAccessType       = pAccessPara->bAccessType;
        file_obj->bIncrement        = pAccessPara->bIncrement;
        file_obj->dwAddressMask     = pAccessPara->bAccessType - 1;

        // honor backward compatibility
        if (file_obj->bAddressModifier & 0x30)
                file_obj->dwAccessBase = 0;
        else
                file_obj->dwAccessBase      = pAccessPara->dwAccessBase;

        // access_type          increment
        //      1                               0,1,2,3,4
        //      2                               0,2,4
        //      4                               0,4

        if (pAccessPara->bIncrement % pAccessPara->bAccessType)
                Status = STATUS_DATATYPE_MISALIGNMENT;

        switch (pAccessPara->bAccessType)
        {
                case BYTE_ACCESS: file_obj->fRead  = readByte;
                                                  file_obj->fWrite = writeByte;
                                                  break;
                case WORD_ACCESS: file_obj->fRead  = readWord;
                                                  file_obj->fWrite = writeWord;
                                                  break;
                case LONG_ACCESS: if (pciada->bWordMode)
                                                  {
                                                        file_obj->fRead  = readWord;
                                                        file_obj->fWrite = writeWord;
                                                  }
                                                  else
                                                  {
                                                        file_obj->fRead  = readLong;
                                                        file_obj->fWrite = writeLong;
                                                  }
                                                  break;
                default: Status = STATUS_UNSUCCESSFUL; break;
        }
        // do here in between what has to be done end -------------

        COMPLETE_REQUEST;

    KdPrint(("ioctl_access_para(), Status = 0x%08x\n", Status));

        return Status;
}

//------------------------------------------------------------------------
// the ultimate jumptable for ioctl
//
NTSTATUS (*ioctl[])(PDEVICE_OBJECT device_Obj, PIRP Irp, PIO_STACK_LOCATION IrpStack) =  
{
        ioctl_init_hardware,                    // 0
        ioctl_deinit_hardware,                  // 1
        ioctl_dummy,                                    // 2
        ioctl_dummy,                                    // 3
        ioctl_get_static_status,                // 4
        ioctl_get_dynamic_status,               // 5
        ioctl_read_vector,                              // 6
        ioctl_access_VIC68A,                    // 7
        ioctl_dummy,                                    // 8
        ioctl_control_interrupts,       // 9
        ioctl_TAS,                                              // 10
        ioctl_dummy,                                    // 11
        ioctl_reset,                                    // 12
        ioctl_access_para                               // 13
};