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