Subversion Repositories f9daq

Rev

Rev 46 | Blame | Compare with Previous | Last modification | View Log | RSS feed

//-------------------------------------------------------------------------
// WINNT driver for PCIVME interface from ARW Elektronik, Germany ---------
// the main body of the driver
//
// (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_drv.c,v $
// Revision 1.3  2004/07/24 07:07:26  klaus
// Update copyright to 2004
//
//
// what                                            who          when
// started                                         AR           15.06.1999
// first release 1.0                               AR           17.10.1999
// fixed error in PLX9050Bug                       AR           28.02.2000
// PLX9050Bugfix bug fixed                         AR           03.03.2000
// PCICC32 CAMAC Interface conflict solved         AR           03.03.2000
// Version 1.1 released                            AR           03.03.2000
// register all used resources, the idle too       AR           25.11.2001
// changed resource allocation caused by WIN2000   AR           08.06.2002
//

//-------------------------------------------------------------------------
// INCLUDES
//
#include <initguid.h>
#include <wdmguid.h>
#include <ntddk.h>
#include <devioctl.h>
#include <pcivme_drv.h>
#include <pcivme_v.h>
#include <pcivme_io.h>
#include <pcivme_i.h>
#include <pcivme.h>
#include <pciif.h>
#include <pcivme_fifo.h>
//------------------------------------------------------------------------
// DEFINES
//
#define arraysize(p) (sizeof(p)/sizeof((p)[0]))

#ifndef DWORD
#define DWORD ULONG
#endif

#ifndef WORD
#define WORD USHORT
#endif

#define CTL_INDEX(x) ((x >> 2) & 0x7FF)  // get user control code as index
#define IRQ_LIST_LENGTH 128     // max count of irqs in FIFO for each file_obj

#define RESOURCE_ENTRY_COUNT 6          // WIN2000 forces to claim all entries

#define DOS_DEVICE_NAME L"\\DosDevices\\PCIVME:"

//------------------------------------------------------------------------
// GLOBALS
//

//------------------------------------------------------------------------
// FUNCTIONS
//

//------------------------------------------------------------------------
// exchange the pointer to Bus Error
//
PBOOLEAN ExchangePointer(PBOOLEAN *current, PBOOLEAN next)
{
        PBOOLEAN pb;

        pb       = *current;
        *current = next;

        return pb;
}

//------------------------------------------------------------------------
// get the vmemm number out of the filename
//
NTSTATUS InterpreteFileName(PCHAR name, int *nVmemm)
{
        char *ptr = name;
    char *n   = "vmemm";
        int  h = -1;  // high part
        int  l = -1;  // low part

        if (*ptr == '\\') ptr++; // jump over leading ...

    while (*n)                           // compare the basename
          if (*n == tolower(*ptr))     
          {
                  n++;
                  ptr++;
          }
          else
                  return STATUS_NO_SUCH_FILE;

        h = *ptr - '0';                  // get the number
        ptr++;
        l = *ptr - '0';

        if (*ptr == 0)                   // still over the end ??
        {
                l = h;
                h = 0;
        }
        else
          ptr++;

        if ((h < 0) || (l < 0) || (*ptr != 0))  // anything wrong ??
                  return STATUS_NO_SUCH_FILE;

        *nVmemm = (h * 10) + l;  // calculate number

        if (*nVmemm >= PCIVME_MAX_VMEMM) // out of range ??
                  return STATUS_NO_SUCH_FILE;

        return STATUS_SUCCESS;
}

//------------------------------------------------------------------------
// the ultimate driver unload
VOID PCIVMEUnload(PDRIVER_OBJECT driverObj)
{
        int              i;
        UNICODE_STRING symbol_name;
        if (!driverObj->DeviceObject) {
                KdPrint(("PCIVMEUnload() DeviceObject does not exist \n"));
                return;
        }
        DEVICE_EXT *ext = (DEVICE_EXT*)(driverObj->DeviceObject->DeviceExtension);
        int         nPCIADAs = ext->nPCIADAs;
        PCIADA      *pciada;

        KdPrint(("PCIVMEUnload() InitState %d \n", ext->nInitState ));


        if (ext->nInitState!=100) return;

        switch (ext->nInitState)
        {
                case 8:
                case 7:
                        // stop interrupts and shut off
                        PCIVMEDeInitPCIADAs(driverObj->DeviceObject);
                        PCIVMEDisConnectInterrupt(driverObj->DeviceObject);

                        // remove interrupt lists
                        for (i = 0; i < nPCIADAs; i++)
                        {
                                pciada = &ext->pciada[i];

                                // removeQueueFromList(...)
                                while (IsListEmpty(&pciada->IrqListList) == FALSE)
                                {
                                        PLIST_ENTRY pList;
                                        FIFO_LIST   *next;
                               
                                        KdPrint(("RemoveHeadList(0x%08x)\n", &pciada->IrqListList));
                                        pList = RemoveHeadList(&pciada->IrqListList);
                                        next  = CONTAINING_RECORD(pList, FIFO_LIST, entry);

                                        ExFreePool((PVOID)next);
                                }
                        }
                case 6:
                        // InitializeIRPQueue has no counterpart
                case 5:
                        // KeInitializeDpc has no counterpart
                case 4:
                        // release io spaces
                        for (i = 0; i < nPCIADAs; i++)
                        {
                                pciada = &ext->pciada[i];

                                if (pciada->pvVirtLcr != NULL)
                                        MmUnmapIoSpace(pciada->pvVirtLcr, LCR_SPACE);
                                if (pciada->pvVirtIfr != NULL)
                                        MmUnmapIoSpace(pciada->pvVirtIfr, IFR_SPACE);
                        }
                case 3:
                        // HalGetInterruptVector has no counterpart
                case 2:
                        // HalTranslateBusAddress has no counterpart
                case 1:
                        // PCIVMEFreeResources(driverObj->DeviceObject); // not used in pnp
                default:
                case 0:
                        RtlInitUnicodeString(&symbol_name, DOS_DEVICE_NAME);

                        // delete the symbolicLink in the registry
                        IoDeleteSymbolicLink( &symbol_name);

                        // delete the deviceObject

                        if (ext->LowerDeviceObject) IoDetachDevice(ext->LowerDeviceObject);
                        IoDeleteDevice(driverObj->DeviceObject);
        }
       
        KdPrint(("PCIVMEUnload() OK.\n"));
}


//------------------------------------------------------------------------
// called at CreateFile()
NTSTATUS PCIVMEOpen(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
        NTSTATUS result = STATUS_SUCCESS;
    ANSI_STRING name;
    int nVmemm;
        int i;
        DEVICE_EXT      *pDevExt = (DEVICE_EXT *)(deviceObj->DeviceExtension);
        PCIADA      *pciada;
        FILE_OBJ        *file_obj = (FILE_OBJ *)NULL;

        name.Buffer = NULL;
    name.MaximumLength = 80;

        result = RtlUnicodeStringToAnsiString(&name, &(Irp->Tail.Overlay.OriginalFileObject->FileName), TRUE);
        if (result != STATUS_SUCCESS) goto fin;

        result = InterpreteFileName(name.Buffer, &nVmemm);
    if (result != STATUS_SUCCESS) goto fin;

    KdPrint(("PCIVMEOpen(%d)\n", nVmemm));
        RtlFreeAnsiString(&name);

    file_obj = (FILE_OBJ *)ExAllocatePoolWithTag(NonPagedPool, sizeof(FILE_OBJ),'nepo');
        if (file_obj == (FILE_OBJ *)NULL)
        {
                result = STATUS_NO_MEMORY;
                goto fin;
        }

        file_obj->uwAssociatedVMEMM = (USHORT) nVmemm;
        file_obj->bAddressModifier  = 0x39;
        file_obj->bAccessType       = BYTE_ACCESS;
        file_obj->bIncrement        = BYTE_ACCESS; // increments each byte
        file_obj->dwAccessBase      = 0;           // normal setting for all but extended

        file_obj->bQueueIrq                     = FALSE;
        result = InitializeFIFO(IRQ_LIST_LENGTH, &file_obj->pIrqListHandle);
        if (result != STATUS_SUCCESS) goto fin;

    Irp->Tail.Overlay.OriginalFileObject->FsContext = (PVOID)file_obj;

        result = PCIVMEScanVMEMM(deviceObj);
        if (result != STATUS_SUCCESS) goto fin;

        for (i = 0; i < pDevExt->nPCIADAs; i++)
        {
          pciada = &pDevExt->pciada[i];

          if (pciada->wModuleNumber == nVmemm)
          {
                  pDevExt->vmemm[nVmemm] = pciada; // create association
                  pciada->dwLinkCount++;
                  break;
          }
        }

        if (i >= pDevExt->nPCIADAs)
        {
                result = STATUS_NO_SUCH_FILE;
                goto fin;
        }

        fin:
        Irp->IoStatus.Status = result;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        // be careful when releasing allocated memory !
        if (result != STATUS_SUCCESS)
                if (file_obj != (FILE_OBJ *)NULL)
                        if (file_obj->pIrqListHandle != (PVOID)NULL)
                                DestroyFIFO(file_obj->pIrqListHandle);

        return result;
}

//------------------------------------------------------------------------
// called at close()
NTSTATUS PCIVMEClose(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
        DEVICE_EXT      *pDevExt  = (DEVICE_EXT *)(deviceObj->DeviceExtension);
        FILE_OBJ    *file_obj = (FILE_OBJ *)NULL;
        PCIADA      *pciada;                   

        file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;

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

        if (file_obj != (FILE_OBJ *)NULL)
        {
                pciada = pDevExt->vmemm[file_obj->uwAssociatedVMEMM];
                pciada->dwLinkCount--;

                // remove the ListEntry(s) associated with this path
        removeQueueFromList(file_obj, pciada);

            // empty and remove the interrupt queue (if there is anything stored)
                DestroyFIFO(file_obj->pIrqListHandle);
               
                ExFreePool(file_obj);
                Irp->Tail.Overlay.OriginalFileObject->FsContext = (FILE_OBJ *)NULL;
        }
 
        KdPrint(("PCIVMEClose OK\n"));

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

        return STATUS_SUCCESS;
}

//------------------------------------------------------------------------
// called at
NTSTATUS PCIVMEShutdown(PDEVICE_OBJECT deviceObj, PIRP  irp  )
{
        UNREFERENCED_PARAMETER(irp);

    KdPrint(("PCIVMEShutdown()\n"));

        // deinit interfaces and interrupts
        PCIVMEDeInitPCIADAs(deviceObj);

    KdPrint(("PCIVMEShutdown() OK\n"));

        return STATUS_SUCCESS;
}

//------------------------------------------------------------------------
// called at ioctl()
NTSTATUS PCIVMEDeviceControl(PDEVICE_OBJECT deviceObj, PIRP Irp)
{
        PIO_STACK_LOCATION IrpStack;
        int   nIndex;

        IrpStack  = IoGetCurrentIrpStackLocation(Irp);
        nIndex    = CTL_INDEX(IrpStack->Parameters.DeviceIoControl.IoControlCode);

    KdPrint(("PCIVMEDeviceControl(%d / 0x%08x)\n", nIndex, Irp->Tail.Overlay.OriginalFileObject));

        if (nIndex > CTL_INDEX(PCIVME_LAST_CTL_CODE))
        {
                Irp->IoStatus.Status      = STATUS_UNSUCCESSFUL;
                Irp->IoStatus.Information = 0;
                IoCompleteRequest(Irp,IO_NO_INCREMENT);

        KdPrint(("PCIVMEDeviceControl() FAIL.\n"));

                return STATUS_UNSUCCESSFUL;
        }

        return ioctl[nIndex](deviceObj, Irp, IrpStack);
}


//------------------------------------------------------------------------
// called at read()
NTSTATUS PCIVMERead(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        NTSTATUS        Status        = STATUS_SUCCESS;
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
        PVOID           pOutputBuffer = ((void *)(MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)));
        LARGE_INTEGER *fileOffset = (LARGE_INTEGER *)&Irp->Tail.Overlay.OriginalFileObject->CurrentByteOffset;
        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];
        register ULONG Address  = IrpStack->Parameters.Read.ByteOffset.LowPart  + file_obj->dwAccessBase;  
        ULONG storeLength                   = 0;
        PBOOLEAN pbPrevBusError;

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

        // do here in between what has to be done -----------------
  if (Address & file_obj->dwAddressMask)     // don't do unaligned transfers
                Status = STATUS_DATATYPE_MISALIGNMENT;
        else
        {
                register ULONG Length = IrpStack->Parameters.Read.Length;    
                register ULONG blockLength;
                register ULONG pageAddress;
                register ULONG toNextPage;
                KIRQL              oldIrql;

                Length     &= ~file_obj->dwAddressMask; // align to integer increments
                storeLength = Length;
               
                // lock other users out
                KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                // check for modifier
                if (pciada->bModifier != file_obj->bAddressModifier)
                {
                        WRITE_REGISTER_UCHAR(pciada->pbModifier, file_obj->bAddressModifier);
                        pciada->bModifier = file_obj->bAddressModifier;
                }
               
                // do the read ---
                file_obj->bBusError = FALSE;
                pbPrevBusError = ExchangePointer(&pciada->pbBusError, &file_obj->bBusError);
                while (Length)
                {
                        pageAddress = Address & ~VME_ADR_MASK;
                        if (pageAddress != pciada->dwVMEPage)
                        {
                                WRITE_REGISTER_ULONG(pciada->pdwVMEAdr, pageAddress);
                                pciada->dwVMEPage = pageAddress;
                        }

                        toNextPage  = (pageAddress + VME_ADR_MASK + 1) - Address;
                        blockLength = (toNextPage < Length) ? toNextPage : Length;

                        KdPrint(("Address 0x%08x, blockLength %d, Length %d\n",
                                                                                Address, blockLength, Length));

                        file_obj->fRead(pOutputBuffer , blockLength, (PVOID)((PUCHAR)pciada->pvVME + (Address & VME_ADR_MASK)));

                        Length  -= blockLength;
                        Address += blockLength;
                        pOutputBuffer = (PVOID)((PUCHAR)pOutputBuffer + blockLength);
                }

                // release the lock
                KeReleaseSpinLock(&pciada->AccessLock, oldIrql);
                ExchangePointer(&pciada->pbBusError, pbPrevBusError);
                if (file_obj->bBusError) Status = STATUS_ACCESS_VIOLATION;
 
            if (file_obj->bIncrement)  // only when increment to next is on
                        *fileOffset =
                                RtlLargeIntegerAdd(*fileOffset, RtlConvertUlongToLargeInteger(storeLength));
        }
        // do here in between what has to be done end -------------

        Irp->IoStatus.Status      = Status;  
        Irp->IoStatus.Information = storeLength;  
        IoCompleteRequest(Irp,IO_NO_INCREMENT);  

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

        return Status;
}


//------------------------------------------------------------------------
// called at write()
NTSTATUS PCIVMEWrite(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        NTSTATUS        Status        = STATUS_SUCCESS;
        PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
        PVOID           pInputBuffer = ((void *)(MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority)));
        LARGE_INTEGER *fileOffset = (LARGE_INTEGER *)&Irp->Tail.Overlay.OriginalFileObject->CurrentByteOffset;
        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];
        register ULONG Address  = IrpStack->Parameters.Write.ByteOffset.LowPart + file_obj->dwAccessBase;  
        ULONG storeLength                   = 0;
        PBOOLEAN pbPrevBusError;

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

        // do here in between what has to be done -----------------
  if (Address & file_obj->dwAddressMask)     // don't do unaligned transfers
                Status = STATUS_DATATYPE_MISALIGNMENT;
        else
        {
                register ULONG Length = IrpStack->Parameters.Write.Length;    
                register ULONG blockLength;
                register ULONG pageAddress;
                register ULONG toNextPage;
                KIRQL              oldIrql;

                Length     &= ~file_obj->dwAddressMask; // align to integer increments
                storeLength = Length;
                                                       // check for modifier
                // lock other users out
                KeAcquireSpinLock(&pciada->AccessLock, &oldIrql);

                if (pciada->bModifier != file_obj->bAddressModifier)
                {
                        WRITE_REGISTER_UCHAR(pciada->pbModifier, file_obj->bAddressModifier);
                        pciada->bModifier = file_obj->bAddressModifier;
                }
               
                // do the read ---
                file_obj->bBusError = FALSE;
                pbPrevBusError = ExchangePointer(&pciada->pbBusError, &file_obj->bBusError);
                while (Length)
                {
                        pageAddress = Address & ~VME_ADR_MASK;
                        if (pageAddress != pciada->dwVMEPage)
                        {
                                WRITE_REGISTER_ULONG(pciada->pdwVMEAdr, pageAddress);
                                pciada->dwVMEPage = pageAddress;
                        }

                        toNextPage  = (pageAddress + VME_ADR_MASK + 1) - Address;
                        blockLength = (toNextPage < Length) ? toNextPage : Length;

                        KdPrint(("Address 0x%08x, blockLength %d, Length %d\n",
                                                                                Address, blockLength, Length));

                        file_obj->fWrite((PVOID)((PUCHAR)pciada->pvVME + (Address & VME_ADR_MASK)) , blockLength, pInputBuffer);

                        Length  -= blockLength;
                        Address += blockLength;
                        pInputBuffer = (PVOID)((PUCHAR)pInputBuffer + blockLength);
                }

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

                if (file_obj->bIncrement)  // only when increment to next is on
                        *fileOffset =   RtlLargeIntegerAdd(*fileOffset, RtlConvertUlongToLargeInteger(storeLength));
        }
        // do here in between what has to be done end -------------

        Irp->IoStatus.Status      = Status;  
        Irp->IoStatus.Information = storeLength;  
        IoCompleteRequest(Irp,IO_NO_INCREMENT);  

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

        return Status;
}

#ifdef DO_CLEANUP
//------------------------------------------------------------------------
// called at cancel of a path
NTSTATUS PCIVMECancel(PDEVICE_OBJECT device_Obj, PIRP Irp)
{
        FILE_OBJ *file_obj = (FILE_OBJ *)Irp->Tail.Overlay.OriginalFileObject->FsContext;
        PIRP     pIrpCancel;

        KdPrint(("PCIVMECancel()\n"));

        // remove all queued IRPs of this file_obj
        do
        {
                pIrpCancel = RemoveIRPfromQueue(device_Obj, file_obj);

                if (pIrpCancel == (PIRP)NULL)
                {
                        IoReleaseCancelSpinLock(pIrpCancel->CancelIrql);
                        break;
                }
                else
                {
                        IoAcquireCancelSpinLock(pIrpCancel->CancelIrql);

                        // mark irp as not pending
                        IoSetCancelRoutine(pIrpCancel, NULL);

                        IoReleaseCancelSpinLock(pIrpCancel->CancelIrql);

                        pIrpCancel->IoStatus.Status = STATUS_CANCELLED;
                        pIrpCancel->IoStatus.Information = 0;
                }
        } while (pIrpCancel != (PIRP)NULL);

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        KdPrint(("PCIVMECancel(OK)\n"));

        return STATUS_SUCCESS;
}
#endif


// http://read.pudn.com/downloads148/sourcecode/windows/vxd/640413/agp/agplib/init.c__.htm
NTSTATUS  
QueryBusInterface(  
    IN PDEVICE_OBJECT DeviceObject,  
    OUT PBUS_INTERFACE_STANDARD BusInterface  
    )  
/*++  
 
Routine Description:  
 
    Sends a query-interface IRP to the specified device object  
    to obtain the BUS_INTERFACE_STANDARD interface.  
 
Arguments:  
 
    DeviceObject - Supplies the device object to send the BUS_INTERFACE_STANDARD to  
 
    BusInterface - Returns the bus interface  
 
Return Value:  
 
    STATUS_SUCCESS if successful  
    NTSTATUS if unsuccessful  
 
--*/
 
   
{  
    PIRP Irp;  
    KEVENT Event;  
    PIO_STACK_LOCATION IrpSp;  
    IO_STATUS_BLOCK IoStatusBlock;  
    NTSTATUS Status;  
//    ULONG ReturnLength;  
   
    KeInitializeEvent( &Event, NotificationEvent, FALSE );  
    Irp = IoBuildSynchronousFsdRequest( IRP_MJ_PNP,  
                                        DeviceObject,  
                                        NULL,  
                                        0,  
                                        NULL,  
                                        &Event,  
                                        &IoStatusBlock );  
    if (Irp == NULL) {  
        return(STATUS_INSUFFICIENT_RESOURCES);  
    }  
   
    IrpSp = IoGetNextIrpStackLocation( Irp );  
    ASSERT(IrpSp != NULL);  
    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED ;  
    IrpSp->MajorFunction = IRP_MJ_PNP;  
    IrpSp->MinorFunction = IRP_MN_QUERY_INTERFACE;  
    IrpSp->Parameters.QueryInterface.InterfaceType = (LPGUID)&GUID_BUS_INTERFACE_STANDARD;  
    IrpSp->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD);  
    IrpSp->Parameters.QueryInterface.Version = 1;  
    IrpSp->Parameters.QueryInterface.Interface = (PINTERFACE) BusInterface;  
    IrpSp->Parameters.QueryInterface.InterfaceSpecificData = NULL;  
   
    Status = IoCallDriver(DeviceObject, Irp);  
    if (Status == STATUS_PENDING) {  
        KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );  
        Status = Irp->IoStatus.Status;  
    }  
   
    return(Status);  
}  
   
//------------------------------------------------------------------------
// search for pciada's
//

NTSTATUS SearchDevices(PDEVICE_OBJECT device_Obj)
{
  PCI_SLOT_NUMBER   SlotNumber;
  PCI_COMMON_CONFIG pci_config;
  PBUS_INTERFACE_STANDARD busInterface;
  PCIADA            *pciada;
 
  ULONG   propertyAddress, length;
  USHORT  FunctionNumber, DeviceNumber;
  int               *found;
  int               i;
  NTSTATUS          status;
  int  BytesRead;
  KdPrint(("SearchDevices()\n"));

  // prepare structures ----------------------------------------
  found = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->nPCIADAs;
  *found = 0;
  for (i = 0; i < PCIVME_MAX_PCIADA; i++)
  {
        pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];

        pciada->Bus                = -1;
    pciada->Slot.u.AsULONG     = 0xFFFFFFFF;
  }

  // search for pciada's ---------------------------------------
  SlotNumber.u.bits.Reserved = 0;
  busInterface = (PBUS_INTERFACE_STANDARD)ExAllocatePool(NonPagedPool,
          sizeof(BUS_INTERFACE_STANDARD));
  if (busInterface == NULL)
  {
          return STATUS_INSUFFICIENT_RESOURCES;
  }
  ((DEVICE_EXT*)(device_Obj->DeviceExtension))->busInterface = busInterface;
  status = QueryBusInterface(device_Obj, busInterface);
  BytesRead = busInterface->GetBusData(busInterface->Context, PCI_WHICHSPACE_CONFIG, &pci_config, 0, PCI_COMMON_HDR_LENGTH);

  pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[0];

                //http://www.hollistech.com/Resources/Misc%20articles/getbusdata.htm
                //IoGetDeviceProperty
                //There are other differnt methods to send a request to lower layer. One method is
        //by sending IRP_MN_READ_CONFIG to lower driver. You will recieve
        //PCI_COMMON_CONFIG structure.
                // http://msdn.microsoft.com/en-us/library/windows/hardware/ff536890(v=vs.85).aspx
                // http://www.rdos.net/svn/tags/V9.2.5/watcom/bld/src/win32/miniwdm/dev/wdmdev.c
 
                if ((pci_config.VendorID    == PCIVME_VENDOR_ID) &&
                        (pci_config.DeviceID    == PCIVME_DEVICE_ID) &&
                        (pci_config.u.type0.SubSystemID == PCIVME_SUBSYS_ID) &&
                        (pci_config.u.type0.SubVendorID == PCIVME_SUBVEN_ID) &&
                        (pci_config.u.type0.BaseAddresses[2]))
                {
               pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[*found];

               memcpy(&pciada->PCIDevice, &pci_config, sizeof(pci_config));
       
                   IoGetDeviceProperty(device_Obj,
                           DevicePropertyBusNumber,
                           sizeof(ULONG),
                           (PVOID)&(pciada->Bus),
                           &length);

                   IoGetDeviceProperty(device_Obj,
                           DevicePropertyAddress,
                           sizeof(ULONG),
                           (PVOID)&propertyAddress,
                           &length);
                   //
                   // For PCI, the DevicePropertyAddress has device number
                   // in the high word and the function number in the low word.
                   //
                   FunctionNumber = (USHORT)((propertyAddress)& 0x0000FFFF);
                   DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF);
                   pciada->Slot.u.AsULONG = DeviceNumber;
                   KdPrint(("PCIADA found @ Bus/Slot %d/%d.\n", pciada->Bus, pciada->Slot.u.AsULONG));

                   (*found)++;
                   if (*found >= PCIVME_MAX_PCIADA) return STATUS_SUCCESS;
                       
        }
  KdPrint(("SearchDevices() found %d devices\n", (*found)));
  return STATUS_SUCCESS;
}

//---------------------------------------------------------------
// function to call for bug fix of PLX9050 build in bug
//
NTSTATUS PLX9050BugFix(PDEVICE_OBJECT device_Obj)
{
        // http://permalink.gmane.org/gmane.linux.kernel.pci/18419
        DEVICE_EXT *DeviceExtension = (DEVICE_EXT*)device_Obj->DeviceExtension;
        int i;
        ULONG dwData;
        PCIADA *pciada;
        PBUS_INTERFACE_STANDARD busInterface;

        KdPrint(("PLX9050BugFix()\n"));
        busInterface = DeviceExtension->busInterface;
        for (i = 0; i < DeviceExtension->nPCIADAs; i++)
        {
                pciada = &DeviceExtension->pciada[i];

                if ((dwData = pciada->PCIDevice.u.type0.BaseAddresses[0]) & 0x80)
                {      
                        KdPrint(("Changing address 0:0x%p with 4:0x%p\n",
                                pciada->PCIDevice.u.type0.BaseAddresses[0],
                                pciada->PCIDevice.u.type0.BaseAddresses[4]));

                        pciada->PCIDevice.u.type0.BaseAddresses[0] =           // exchange
                                pciada->PCIDevice.u.type0.BaseAddresses[4];
                        pciada->PCIDevice.u.type0.BaseAddresses[4] = dwData;

                        if (busInterface->SetBusData(busInterface->Context, PCI_WHICHSPACE_CONFIG, (PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[0], 0x10, 4) != 4)
                                return STATUS_UNSUCCESSFUL;
                       
                        if (busInterface->SetBusData(busInterface->Context, PCI_WHICHSPACE_CONFIG, (PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[4], 0x20, 4) != 4)
                                return STATUS_UNSUCCESSFUL;
                       
                }
     
                if ((dwData = pciada->PCIDevice.u.type0.BaseAddresses[1]) & 0x80)
                {
                        KdPrint(("Changing address 1:0x%p with 5:0x%p\n",
                                pciada->PCIDevice.u.type0.BaseAddresses[1],
                                pciada->PCIDevice.u.type0.BaseAddresses[5]));

                  pciada->PCIDevice.u.type0.BaseAddresses[1] =           // exchange
                                pciada->PCIDevice.u.type0.BaseAddresses[5];
                  pciada->PCIDevice.u.type0.BaseAddresses[5] = dwData;

                  if (busInterface->SetBusData(busInterface->Context, PCI_WHICHSPACE_CONFIG, (PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[1], 0x14, 4) != 4)
                          return STATUS_UNSUCCESSFUL;
                 
                  if (busInterface->SetBusData(busInterface->Context, PCI_WHICHSPACE_CONFIG, (PVOID)&pciada->PCIDevice.u.type0.BaseAddresses[5], 0x24, 4) != 4)
                          return STATUS_UNSUCCESSFUL;
                 
                }
        }

        return STATUS_SUCCESS;
}

static VOID ShowResources(IN PCM_PARTIAL_RESOURCE_LIST list)
{                                                       // ShowResources
        PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = list->PartialDescriptors;
        ULONG nres = list->Count;
        ULONG i;

        for (i = 0; i < nres; ++i, ++resource)
        {                                               // for each resource
                ULONG type = resource->Type;

                static char* namelow[] = {
                        "CmResourceTypeNull",//                0   // ResType_All or ResType_None (0x0000)
                        "CmResourceTypePort",//               1   // ResType_IO (0x0002)
                        "CmResourceTypeInterrupt",//          2   // ResType_IRQ (0x0004)
                        "CmResourceTypeMemory",//             3   // ResType_Mem (0x0001)
                        "CmResourceTypeDma",//                4   // ResType_DMA (0x0003)
                        "CmResourceTypeDeviceSpecific",//      5   // ResType_ClassSpecific (0xFFFF)
                        "CmResourceTypeBusNumber",//          6   // ResType_BusNumber (0x0006)
                        "CmResourceTypeMemoryLarge" //        7   // ResType_MemLarge (0x0007)
                };
                static char* namehigh[] = {
                        //"CmResourceTypeNonArbitrated" ,//    128   // Not arbitrated if 0x80 bit set
                        "CmResourceTypeConfigData",//       128   // ResType_Reserved (0x8000)
                        "CmResourceTypeDevicePrivate",//    129   // ResType_DevicePrivate (0x8001)
                        "CmResourceTypePcCardConfig",//     130   // ResType_PcCardConfig (0x8002)
                        "CmResourceTypeMfCardConfig",//     131   // ResType_MfCardConfig (0x8003)
                        "CmResourceTypeConnection" //       132   // ResType_Connection (0x8004)
                };

                if (type<8) KdPrint((" [%d] type %s", type, type < arraysize(namelow) ? namelow[type] : "unknown"));
                if (type>127) KdPrint((" [%d] type %s", type, type-128 < arraysize(namehigh) ? namehigh[type-128] : "unknown"));
                switch (type)
                {                                       // select on resource type
                case CmResourceTypePort:
                                KdPrint((" start ADDR=0x%p  0x%8X%8.8lX length %X\n",
                                resource->u.Port.Start, resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart,
                                resource->u.Port.Length));
                        break;
                case CmResourceTypeMemory:
                        KdPrint((" %d start ADDR=0x%p \n", i,  resource->u.Memory.Start ));
                        break;

                case CmResourceTypeInterrupt:
                        KdPrint(("  IRQL %d, vector %d, affinity %d\n",
                                resource->u.Interrupt.Level, resource->u.Interrupt.Vector,
                                resource->u.Interrupt.Affinity));
                        break;

                case CmResourceTypeDma:
                        KdPrint(("  channel %d, port %X\n",
                                resource->u.Dma.Channel, resource->u.Dma.Port));

                       
                       
                }                                       // select on resource type
        }                                               // for each resource
}                                                       // ShowResources


//------------------------------------------------------------------------
//  reserve resources for PCIADAs
//
NTSTATUS PCIVMEExtractResources(PCIADA *pciada, PIRP irp)
{
        //PCM_RESOURCE_LIST pResourceList;
        ///PCM_FULL_RESOURCE_DESCRIPTOR pFullDescriptor;
        PCM_PARTIAL_RESOURCE_LIST pPartialList = NULL;
        PCM_PARTIAL_RESOURCE_LIST pListTranslated = NULL;
        PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialDescriptor;
        int i;
        int bug = 0;

        KdPrint(("PCIVMEExtractResources()\n"));

        ///pResourceList   = pList;
        ///pFullDescriptor = pResourceList->List;
        ///pPartialList    = &pFullDescriptor->PartialResourceList;
        PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(irp);
        if (stack->Parameters.StartDevice.AllocatedResources)
        pPartialList = &stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList;
        KdPrint(("Allocated Resources:------------------\n"));
        ShowResources(pPartialList);
       
        int plcount = 0;
        for (i=0; i<(int)pPartialList->Count; i++)
        {
                pPartialDescriptor = &pPartialList->PartialDescriptors[i];
               
                switch (pPartialDescriptor->Type)
                {
                        case CmResourceTypeInterrupt:
                                pciada->Irql     = (KIRQL)pPartialDescriptor->u.Interrupt.Level;
                                pciada->Vector   = pPartialDescriptor->u.Interrupt.Vector;
                                pciada->Affinity = pPartialDescriptor->u.Interrupt.Affinity;

                                KdPrint(("AllocatedResources Irq    : Irql: %d, Vector: %d, Affinity: %d\n",
                                        pciada->Irql, pciada->Vector, pciada->Affinity));
                                break;
                        case CmResourceTypeDma:
                                KdPrint(("AllocatedResources Dma    : \n"));
                                break;
                        case CmResourceTypePort:

                                KdPrint(("AllocatedResources Port   : 0x%p\n", pPartialDescriptor->u.Port.Start));
                                break;
                        case CmResourceTypeMemory:
                                // special handling of PLXBUG here because of WIN2000
                                // WIN2000 doesn't recognize late address changes
                                if (!bug)
                                {
                                        if (plcount == 0)
                                        {
                                                pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;

                                                if (pciada->pvPhysLcr.LowPart & 0x80) {
                                                        bug = 1;
                                                        KdPrint(("AllocatedResources PLXBug\n"));
                                                }
                                        }
                                }
                                else
                                {
                                        if (plcount == 3)
                                                pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;
                                }

                                if (plcount == 2){
                                        pciada->pvPhysIfr = pPartialDescriptor->u.Memory.Start;
                                        KdPrint(("PCIVMEExtractResources() IFR=0x%p \n", pciada->pvPhysIfr));
                                }
                                KdPrint(("[%d] AllocatedResources Memory : 0x%p\n", plcount, (PUCHAR)pPartialDescriptor->u.Memory.Start.LowPart));
                                break;
                }
                if (pPartialDescriptor->Type < 8) plcount++;
        }

        KdPrint(("PCIVMEExtractResources() LCR=0x%p IFR=0x%p \n", pciada->pvPhysLcr, pciada->pvPhysIfr));

        if (stack->Parameters.StartDevice.AllocatedResourcesTranslated)
                pListTranslated = &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList;
        KdPrint(("Translated Resources:------------------\n"));
        ShowResources(pListTranslated);
        plcount = 0;
        bug     = 0;
        for (i = 0; i<(int)pPartialList->Count; i++)
        {
                pPartialDescriptor = &pListTranslated->PartialDescriptors[i];

                switch (pPartialDescriptor->Type)
                {
                case CmResourceTypeInterrupt:
                        pciada->Irql = (KIRQL)pPartialDescriptor->u.Interrupt.Level;
                        pciada->Vector = pPartialDescriptor->u.Interrupt.Vector;
                        pciada->Affinity = pPartialDescriptor->u.Interrupt.Affinity;

                        KdPrint(("AllocatedResourcesTranslated Irq    : Irql: %d, Vector: %d, Affinity: %d\n",
                                pciada->Irql, pciada->Vector, pciada->Affinity));
                        break;
                case CmResourceTypeDma:
                        KdPrint(("AllocatedResourcesTranslated Dma    : \n"));
                        break;
                case CmResourceTypePort:

                        KdPrint(("AllocatedResourcesTranslated Port   : 0x%p\n", pPartialDescriptor->u.Port.Start));
                        break;
                case CmResourceTypeMemory:
                        // special handling of PLXBUG here because of WIN2000
                        // WIN2000 doesn't recognize late address changes
                        if (!bug)
                        {
                                if (plcount == 0)
                                {
                                        pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;
                                        KdPrint(("0 PCIVMEExtractResources() LCR=0x%p \n", pciada->pvPhysLcr));
                                        if (pciada->pvPhysLcr.LowPart & 0x80) {
                                                bug = 1;
                                                KdPrint(("AllocatedResourcesTranslated PLXBug\n"));
                                        }
                                }
                        }
                        else
                        {
                                if (plcount == 3){
                                        pciada->pvPhysLcr = pPartialDescriptor->u.Memory.Start;
                                        KdPrint(("3 PCIVMEExtractResources() LCR=0x%p \n", pciada->pvPhysLcr));
                                }
                        }

                        if (plcount == 2){
                                pciada->pvPhysIfr = pPartialDescriptor->u.Memory.Start;
                                KdPrint(("2 PCIVMEExtractResources() IFR=0x%p \n", pciada->pvPhysIfr));
                        }

                        KdPrint(("[%d] AllocatedResourcesTranslated Memory : 0x%p\n", plcount, (PUCHAR)pPartialDescriptor->u.Memory.Start.LowPart));
                        break;
                }
                if (pPartialDescriptor->Type < 8) plcount++;
        }



        if (pciada->Irql == 0)
                return STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT;

        KdPrint(("PCIVMEExtractResources() Translated LCR=0x%p IFR=0x%p \n", pciada->pvPhysLcr, pciada->pvPhysIfr));

        return STATUS_SUCCESS;
}

NTSTATUS PCIVMEReserveResources(PDEVICE_OBJECT device_Obj, PIRP irp)
{
        //PCM_RESOURCE_LIST pList = NULL;
        NTSTATUS result = STATUS_SUCCESS;
        int i;
        DEVICE_EXT *pDevExt = (DEVICE_EXT*)(device_Obj->DeviceExtension);
        int nPCIADAs = pDevExt->nPCIADAs;
        PCIADA *pciada;
    UNICODE_STRING DriverClassName;
   
        KdPrint(("PCIVMEReserveResources()\n"));

        // prepare resource claiming
        //RtlInitUnicodeString(&DriverClassName, L"PCICC32");
        RtlInitUnicodeString(&DriverClassName, L"PCIVME");
        // cycle through all busses and slots assigned to PCIADAs
        for (i = 0; i < nPCIADAs; i++)
    {
                pciada = &pDevExt->pciada[i];

                //result = HalAssignSlotResources(NULL, &DriverClassName, device_Obj->DriverObject, device_Obj,
                //      PCIBus, pciada->Bus, pciada->Slot.u.AsULONG, &pList);
                /*
                result = IoReportDetectedDevice(device_Obj->DriverObject, PCIBus, pciada->Bus, pciada->Slot.u.AsULONG, pList, NULL, FALSE, &device_Obj);
                if (result != STATUS_SUCCESS){
                        KdPrint(("PCIVMEReserveResources(0x%08x) HalAssignSlotResources  ***ERROR*** faliure to get slot information\n", result));
                        break;
                }
                */

                result = PCIVMEExtractResources(pciada, irp);

                if (result != STATUS_SUCCESS){
                        KdPrint(("PCIVMEReserveResources(0x%08x) PCIVMEExtractResources\n", result));
                        break;
                }
        }

        // its my part to free allocated resources
        //if (pList != NULL) ExFreePoolWithTag(pList,'nepo');

        KdPrint(("PCIVMEReserveResources(0x%08x)\n", result));

        return result;
};


//------------------------------------------------------------------------
//  translate memory resources to neutral for PCIADAs
//
NTSTATUS PCIVMETranslateBusAddresses(PDEVICE_OBJECT device_Obj,  PIRP irp)
{
        int              i;
        NTSTATUS         result = STATUS_SUCCESS;
        int nPCIADAs = ((DEVICE_EXT*)(device_Obj->DeviceExtension))->nPCIADAs;
        ULONG            memType0, memType2;
        PCIADA           *pciada;
        PCM_PARTIAL_RESOURCE_LIST pPartialList = NULL;
        PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(irp);
        if (stack->Parameters.StartDevice.AllocatedResourcesTranslated)
       pPartialList = &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList;

   
        for (i = 0; i < nPCIADAs; i++)
        {
      pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];

          memType0 = memType2 = 0;
          /*
          if (!(HalTranslateBusAddress(PCIBus, pciada->Bus, pciada->pvPhysLcr, &memType0,
                                                               &pciada->pvPhysLcr)) ||
              !(HalTranslateBusAddress(PCIBus, pciada->Bus, pciada->pvPhysIfr, &memType2,
                                                               &pciada->pvPhysIfr)))
          {
                  result = STATUS_UNSUCCESSFUL;
                  break;
          }
          */

          KdPrint(("PCIADA %d TranslateBusAddresseses() -- no translation is made\n", i));

          if ((memType0) || (memType2))
          {
                  result = STATUS_UNSUCCESSFUL;
                  break;
          }
        }
   
        return result;
}

//------------------------------------------------------------------------
//  map address spaces to virtual addresses
//
NTSTATUS PCIVMEMapIOspace(PDEVICE_OBJECT device_Obj)
{
        int              i;
        DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
        int                              nPCIADAs = pDevExt->nPCIADAs;
        PCIADA           *pciada;

        KdPrint(("PCIVMEMapIOspace()\n"));

        for (i = 0; i < nPCIADAs; i++)
        {
       pciada = &pDevExt->pciada[i];

       if ((pciada->pvVirtLcr = MmMapIoSpace(pciada->pvPhysLcr, LCR_SPACE, FALSE)) == NULL)
          return STATUS_UNSUCCESSFUL;
       if ((pciada->pvVirtIfr = MmMapIoSpace(pciada->pvPhysIfr, IFR_SPACE, FALSE)) == NULL)
          return STATUS_UNSUCCESSFUL;

           KdPrint(("PCIADA %d: LCR 0x%08x IFR 0x%08x\n",
                                         i, pciada->pvVirtLcr, pciada->pvVirtIfr));

           pciada->pwIntCSR = (PUSHORT)((PUCHAR)pciada->pvVirtLcr + 0x4C);
           pciada->pwCntrl  = (PUSHORT)((PUCHAR)pciada->pvVirtLcr + 0x50);
        }

        return STATUS_SUCCESS;
}


//------------------------------------------------------------------------
//  initializes and registers a DPC routine for each pciada
//
NTSTATUS InitializeCustomDPCObjects(PDEVICE_OBJECT device_object)
{
        int              i;
        int nPCIADAs = ((DEVICE_EXT*)(device_object->DeviceExtension))->nPCIADAs;
        PCIADA           *pciada;

        KdPrint(("InitializeCustomDPCObject()\n"));

        for (i = 0; i < nPCIADAs; i++)
        {
                pciada = &((DEVICE_EXT*)(device_object->DeviceExtension))->pciada[i];
                KeInitializeDpc(&pciada->kDPCobj, fMyDefferedRoutine, (PVOID)device_object);
        }

        return STATUS_SUCCESS;
}

//------------------------------------------------------------------------
//  initializes the queue for storing IRPs waiting for vectors
//
NTSTATUS InitializeIRPQueue(PDEVICE_OBJECT device_Obj)
{
        DEVICE_EXT *pDevExt = ((DEVICE_EXT*)(device_Obj->DeviceExtension));

        KdPrint(("InitializeIRPQueue()\n"));

        KeInitializeSpinLock(&pDevExt->IRPLock);
        InitializeListHead(&pDevExt->IRPList);

        return STATUS_SUCCESS;
}

//------------------------------------------------------------------------
// init structures a.s.o.
//
VOID PCIVMESoftInit(PDEVICE_OBJECT device_Obj)
{
        int i;
        PCIADA *pciada;

        for (i = 0; i < PCIVME_MAX_PCIADA; i++)
        {
                pciada = &((DEVICE_EXT*)(device_Obj->DeviceExtension))->pciada[i];

                pciada->pvPhysLcr.QuadPart = pciada->pvPhysIfr.QuadPart = 0;
                pciada->pvVirtLcr = pciada->pvVirtIfr = NULL;

                pciada->bConnected     = FALSE;   // connection still not verified
                pciada->bWordMode      = TRUE;
                pciada->bSysControl    = FALSE;
                pciada->wModuleNumber  = 0xFFFF;
                pciada->wFPGAVersion   = 0xFFFF;
                pciada->wModuleType    = 1;       // always VMEMM

                pciada->InterruptObject = NULL;
                pciada->Irql                    = 0;
                pciada->Vector          = 0;
                pciada->Affinity                = 0;

                pciada->dwLinkCount     = 0;
                                       
                KeInitializeSpinLock(&pciada->IrqListLock);
                KeInitializeSpinLock(&pciada->AccessLock);
 
                InitializeListHead(&pciada->IrqListList);  // start of list of irq fifos

                pciada->nInterruptHandlers = 0;
        }
 
        // no vmemm associated to any PCIADA
        for (i = 0; i < PCIVME_MAX_VMEMM; i++)
          ((DEVICE_EXT*)(device_Obj->DeviceExtension))->vmemm[i] = NULL;
}

NTSTATUS OnRequestComplete(PDEVICE_OBJECT fdo, PIRP Irp, PKEVENT pev)
{
        UNREFERENCED_PARAMETER(fdo);
        UNREFERENCED_PARAMETER(Irp);
        KeSetEvent(pev, 0, FALSE);
        return STATUS_MORE_PROCESSING_REQUIRED;
}

NTSTATUS CompleteRequest(PIRP Irp, NTSTATUS status, ULONG_PTR Information)
{
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = Information;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return status;
}
NTSTATUS ForwardAndWait(PDEVICE_OBJECT fdo, PIRP Irp)
{
        KEVENT event;
        KeInitializeEvent(&event, NotificationEvent, FALSE);
        IoCopyCurrentIrpStackLocationToNext(Irp);
        IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnRequestComplete, (PVOID)&event, TRUE, TRUE, TRUE);
        PDEVICE_EXT pdx = (PDEVICE_EXT ) fdo->DeviceExtension;
        IoCallDriver(pdx->LowerDeviceObject, Irp);
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        return Irp->IoStatus.Status;
}

NTSTATUS HandleStartDevice(PDEVICE_OBJECT fdo, PIRP Irp)
{
        PIO_STACK_LOCATION stack;
        Irp->IoStatus.Status = STATUS_SUCCESS;
        NTSTATUS status = ForwardAndWait(fdo, Irp);
        if (!NT_SUCCESS(status))
                return CompleteRequest(Irp, status, Irp->IoStatus.Information);
       
        stack = IoGetCurrentIrpStackLocation(Irp);
        status = PCIVMEStartDevice(fdo, Irp);
        return CompleteRequest(Irp, status, Irp->IoStatus.Information);
}


NTSTATUS DefaultPnpHandler(PDEVICE_OBJECT fdo, PIRP Irp)
{
        IoSkipCurrentIrpStackLocation(Irp);
        PDEVICE_EXT pdx = (PDEVICE_EXT )fdo->DeviceExtension;
        return IoCallDriver(pdx->LowerDeviceObject, Irp);
}

NTSTATUS HandleStopDevice(PDEVICE_OBJECT fdo, PIRP Irp)
{
        IoSkipCurrentIrpStackLocation(Irp);
        UNICODE_STRING symbol_name;
        RtlInitUnicodeString(&symbol_name, DOS_DEVICE_NAME);
    // delete the symbolicLink in the registry
        IoDeleteSymbolicLink(&symbol_name);
        DEVICE_EXT *ext = (DEVICE_EXT*)(fdo->DeviceExtension);
        // delete the busInterface and deviceObject
        if (ext->busInterface) {
                (*ext->busInterface->InterfaceDereference)(ext->busInterface->Context);
                ext->busInterface = NULL;
        }
        if (ext->LowerDeviceObject) IoDetachDevice(ext->LowerDeviceObject);
        IoDeleteDevice(fdo);
        KdPrint(("HandleStopDevice (OK)\n"));
        return STATUS_SUCCESS;
}


NTSTATUS PCIVMEDispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp)
{
        PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
        ULONG fcn = stack->MinorFunction;
        static char* pnpname[] = {
                "IRP_MN_START_DEVICE",
                "IRP_MN_QUERY_REMOVE_DEVICE",
                "IRP_MN_REMOVE_DEVICE",
                "IRP_MN_CANCEL_REMOVE_DEVICE",
                "IRP_MN_STOP_DEVICE",
                "IRP_MN_QUERY_STOP_DEVICE",
                "IRP_MN_CANCEL_STOP_DEVICE",
                "IRP_MN_QUERY_DEVICE_RELATIONS",
                "IRP_MN_QUERY_INTERFACE",
                "IRP_MN_QUERY_CAPABILITIES",
                "IRP_MN_QUERY_RESOURCES",
                "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
                "IRP_MN_QUERY_DEVICE_TEXT",
                "IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
                "",
                "IRP_MN_READ_CONFIG",
                "IRP_MN_WRITE_CONFIG",
                "IRP_MN_EJECT",
                "IRP_MN_SET_LOCK",
                "IRP_MN_QUERY_ID",
                "IRP_MN_QUERY_PNP_DEVICE_STATE",
                "IRP_MN_QUERY_BUS_INFORMATION",
                "IRP_MN_DEVICE_USAGE_NOTIFICATION",
                "IRP_MN_SURPRISE_REMOVAL",
                "IRP_MN_QUERY_LEGACY_BUS_INFORMATION",
        };

        if (fcn < arraysize(pnpname))
                KdPrint(("PCIVMEDispatchPnp  - IRP_MJ_PNP (%s)\n", pnpname[fcn]));
        else
                KdPrint(( "PCIVMEDispatchPnp - IRP_MJ_PNP (%2.2X)\n", fcn));

        static NTSTATUS(*fcntab[])(PDEVICE_OBJECT, PIRP) = {
                HandleStartDevice,        // IRP_MN_START_DEVICE
        //      DefaultPnpHandler,        // IRP_MN_QUERY_REMOVE_DEVICE
                HandleStopDevice          // IRP_MN_REMOVE_DEVICE
        };

        if (fcn >= arraysize(fcntab))
                return DefaultPnpHandler(fdo, Irp);
        return (*fcntab[fcn])(fdo, Irp);
}


NTSTATUS PCIVMEStartDevice(PDEVICE_OBJECT device_object, PIRP irp){
        NTSTATUS result = STATUS_SUCCESS;
        int            nPCIADAs;                                // count of PCIADAs
        DEVICE_EXT     *DeviceExtension = NULL;
        DeviceExtension = (DEVICE_EXT*)device_object->DeviceExtension;
        // init pciada structures ------------------------------------
        PCIVMESoftInit(device_object);

        // search for PCIADAs ----------------------------------------
        result = SearchDevices(device_object);
        nPCIADAs = DeviceExtension->nPCIADAs;

        if ((result != STATUS_SUCCESS) || !(nPCIADAs))
        {
                KdPrint(("PCIVMEStartDevice Device not found\n"));
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }

        // request exclusive ownership of .. ---------------------------------
        if ((result = PCIVMEReserveResources(device_object, irp)) != STATUS_SUCCESS)
        {
                KdPrint(("PCIVMEStartDevice Resource not reserved PCIVMEReserveResources \n"));
                PCIVMEUnload(DeviceExtension->driverObj);
                return result;
        }
        else
                DeviceExtension->nInitState++;
        // fix PLX9050 Bug -------------------------------------------
        if ((result = PLX9050BugFix(device_object)) != STATUS_SUCCESS)
        {
                KdPrint(("PCIVMEStartDevice PLX9050BugFix\n"));
                PCIVMEUnload(DeviceExtension->driverObj);
                return result;
        }

        DeviceExtension->nInitState += 2;
        /*
        // translate BUS relative addresses ----------------------------------
        if ((result = PCIVMETranslateBusAddresses(device_object,irp)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }
        else
                DeviceExtension->nInitState++;

       
        // translate Interrupt Resources used --------------------------------
        if ((result = PCIVMETranslateInterrupts(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }
        else
                DeviceExtension->nInitState++;
        */


        // map address spaces to virtual addresses ---------------------------
        if ((result = PCIVMEMapIOspace(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }
        else
                DeviceExtension->nInitState++;

        // initialze my custom DPC objects -----------------------------------
        if ((result = InitializeCustomDPCObjects(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return result;
        }
        else
                DeviceExtension->nInitState++;

        // initialze the queue for IRPs waiting for vectors ------------------
        if ((result = InitializeIRPQueue(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return result;
        }
        else
                DeviceExtension->nInitState++;

        // connect interrupts to service routines ----------------------------
        if ((result = PCIVMEConnectInterrupt(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }
        else
                DeviceExtension->nInitState++;

        // scan all connected VMEMM for info and later use -------------------
        if ((result = PCIVMEScanVMEMM(device_object)) != STATUS_SUCCESS)
        {
                PCIVMEUnload(DeviceExtension->driverObj);
                return STATUS_DEVICE_DOES_NOT_EXIST;
        }

        device_object->Flags &= ~DO_DEVICE_INITIALIZING;
        return result;
}

NTSTATUS PCIVMEAddDevice(PDRIVER_OBJECT driverObj,  PDEVICE_OBJECT  pdo)
        {                                                       // AddDevice
       
        UNICODE_STRING device_name;
        UNICODE_STRING symbol_name;
        NTSTATUS result = STATUS_SUCCESS;
        //int            nPCIADAs;                              // count of PCIADAs
        DEVICE_EXT     *DeviceExtension = NULL;
        PDEVICE_OBJECT device_object;
        RtlInitUnicodeString(&device_name, L"\\Device\\PCIVME");
        KdPrint(("PCIVME AddDevice() v%d.%d\n", (DRIVER_VERSION >> 16) & 0xff, DRIVER_VERSION & 0xff));
        /* DeviceObject durch IO-Manager erzeugen */
        result = IoCreateDevice( driverObj,           // DriverObject received by the DriverEntry Call
                                                         sizeof(DEVICE_EXT),  // required Memory for the DeviceExtension
                                                         &device_name,        // Name of the device in the device-Directory
                                                         FILE_DEVICE_UNKNOWN, // Device-ID              
                                                         0,                   // Device-Characteristics normal 0
                                                         FALSE,               // TRUE : one Thread can open the driver
                                                         &device_object);     // DeviceObject returned from the IO-Manager

        // defines how the data are handled between user / kernel Adress-Space  
        device_object->Flags |= DO_DIRECT_IO;

#if 0
        // register the shutdown notification entry
    IoRegisterShutdownNotification(device_object);
#endif

        // anounce driver as symbolic device ---------------------------------
        if (result == STATUS_SUCCESS)
        {
                /* now the symbolic Link is created. If there is no S.L. a program cannot connect to the driver */
                RtlInitUnicodeString(&symbol_name, DOS_DEVICE_NAME);
                result = IoCreateSymbolicLink(&symbol_name,&device_name);
                if (result != STATUS_SUCCESS)
                {
       
                        IoDeleteDevice(device_object);
                        return result;
                }
        }
        else
                return result;


        DeviceExtension = (DEVICE_EXT*)device_object->DeviceExtension;

        DeviceExtension->actualIrp  = NULL;
        DeviceExtension->driverObj  = driverObj;
        DeviceExtension->nInitState = 0;

       
        DeviceExtension->LowerDeviceObject = IoAttachDeviceToDeviceStack(device_object, pdo);
    KdPrint(("AddDevice() OK.\n"));

        return result;
}

//------------------------------------------------------------------------
// the ultimate starting point of a driver
NTSTATUS DriverEntry(PDRIVER_OBJECT driverObj, PUNICODE_STRING regPath)
{
        UNREFERENCED_PARAMETER(regPath);
       
        KdPrint(("PCIVME DriverEntry() v%d.%d\n", (DRIVER_VERSION >> 16) & 0xff, DRIVER_VERSION & 0xff));

        driverObj->DriverUnload = PCIVMEUnload;
        driverObj->DriverExtension->AddDevice = PCIVMEAddDevice;
        driverObj->MajorFunction[IRP_MJ_CREATE] = PCIVMEOpen;
        driverObj->MajorFunction[IRP_MJ_CLOSE] = PCIVMEClose;
        driverObj->MajorFunction[IRP_MJ_READ] = PCIVMERead;
        driverObj->MajorFunction[IRP_MJ_WRITE] = PCIVMEWrite;
        driverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PCIVMEDeviceControl;
#ifdef DO_CLEANUP
        driverObj->MajorFunction[IRP_MJ_CLEANUP] = PCIVMECancel;
#endif
        driverObj->MajorFunction[IRP_MJ_SHUTDOWN] = PCIVMEShutdown;
        driverObj->MajorFunction[IRP_MJ_PNP] = PCIVMEDispatchPnp;
        return  STATUS_SUCCESS;;
}