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 ---------
// all around irq handling
//
// (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_i.c,v $
// Revision 1.3  2004/07/24 07:07:26  klaus
// Update copyright to 2004
//
// Revision 1.2  2003/11/15 19:12:50  klaus
// Update copyright to 2003
//
// Revision 1.1.1.1  2003/11/14 23:16:33  klaus
// First put into repository
//
// 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           01.08.1999
// first release 1.0                                                       AR                   17.10.1999
// IoConnectInterrupt, share vector now true       AR           04.03.2000
// changed resource allocation caused by WIN2000   AR           08.06.2002
//
 
//-------------------------------------------------------------------------
// COMMENTS
//
// Each path (file_obj) maintains its own FIFO to hold incoming vector from
// hardware acknowledged interrupts. These FIFOs are always accessible for read
// while the path is open. In case a path enables its interrupts the link to
// its FIFO is included in a list of "interrupt enabled" FIFOs. The opposite
// happens when a path disables its interrupts.
// There are up to MAX_PCIADA lists of "interrupt enabled" FIFOs, each list is
// associated to a PCIADA (pciada).
// Each time a pciada receives a interrupt request the software first checks
// for a bus error interrupt. This kind of requests are directly associated with
// a user initiated access and put forward to the accessor. (Excluding spurious
// interrupts). If the request is no bus error request the vector is pushed
// into all FIFOs queued for this PCIADA.
// After this a DPC call is fired to wake up queued IRPs waiting for a vector.
// The DPC call iterates through the list of FIFOs and looks if a queued IRP
// exists for this path. If so the vector is pulled off the FIFO and the IRP
// is released.
// If a IRP arrives to request a vector the driver checks if there is a vector
// stored in the path associated FIFO. If so the IRP returns immediately with
// the pulled vector from the FIFO. If not the IRP is queued in a PCIADA
// associated queue of pending IRPs.
// When the interrupts for a path are disabled all pending vectors in the FIFO
// associated with this path are still available. But if the FIFO is empty
// request to get the next vector will block.

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

//------------------------------------------------------------------------
// DEFINES
//
#ifndef DWORD
#define DWORD ULONG
#endif

#ifndef WORD
#define WORD USHORT
#endif

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

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

//------------------------------------------------------------------------
// determine which interrupt and evaluates vector
//
static UCHAR evaluate_vector(PCIADA *pciada)
{
        USHORT  wIntCSR = READ_REGISTER_USHORT(pciada->pwIntCSR);
    USHORT  offset;
        USHORT  wCntrl = READ_REGISTER_USHORT(pciada->pwCntrl);

    if (wCntrl & 0x100)   // pciada switched on ?
        {
                if ((wIntCSR & 0x68) == 0x68)
                {
                        // it's the pci interrupt # 2

                        // get current Cntrl - and clear interrupt
                        wCntrl &= ~0x0100;  // disable
                        WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);
                        wCntrl |= 0x0100;   // enable again
                        WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);
                        return 1;
                }

                if ((wIntCSR & 0x45) == 0x45)
                {
                        // it's the interrupt # 1
                        offset = READ_REGISTER_USHORT(pciada->pwIRQStat);

                        if (offset & 1)
                                return 0xff & READ_REGISTER_UCHAR((PUCHAR)pciada->pbVector + offset);
                }
        }
 
        return 0;
}

//------------------------------------------------------------------------
// enable and disable of interrupts
//
void globalInterruptEnable(PCIADA *pciada)
{
   WRITE_REGISTER_USHORT(pciada->pwIntCSR, ENABLE_PCIADA_IRQS);
}

void globalInterruptDisable(PCIADA *pciada)
{
   WRITE_REGISTER_USHORT(pciada->pwIntCSR, DISABLE_PCIADA_IRQS);
}
 
//------------------------------------------------------------------------
// insert the vector in a queue of interrupts (and rescue waiting IRPs)
//
static void InsertInIrqQueues(PCIADA *pciada, UCHAR vector)
{
        FIFO_LIST   *next;
        KIRQL           oldIrqlList;
        register PLIST_ENTRY pList = &pciada->IrqListList;

        KdPrint(("InsertInIrqQueues\n"));
 
        // iterate all fifos and insert the vector in each fifo
        KeAcquireSpinLock(&pciada->IrqListLock, &oldIrqlList);
 
        while (pList->Flink != &pciada->IrqListList)
        {
                pList = pList->Flink;
                next = CONTAINING_RECORD(pList, FIFO_LIST, entry);

                KdPrint(("Vector %d pushed\n", vector));
 
                // push the vector into the fifo
                PushElement(next->pIrqListHandle, vector);
        }
         
        KeReleaseSpinLock(&pciada->IrqListLock, oldIrqlList);

        // more handling in the deffered procedure call
        KeInsertQueueDpc(&pciada->kDPCobj, (PVOID)pciada, (PVOID)&vector);
}
 
//------------------------------------------------------------------------
// main interrupt handler function
//
BOOLEAN irq_service(PKINTERRUPT Interrupt, PVOID ServiceContext)
{
        PCIADA *pciada = (PCIADA *)ServiceContext;
        UCHAR  vector;
        UNREFERENCED_PARAMETER(Interrupt);
        vector = evaluate_vector(pciada);
        if (!(vector)) return FALSE;
   
        KdPrint(("irq_service: %d\n", vector & 0xff));

        if ((pciada->pbBusError) && ((vector == 7) || (vector == 1)))
                *pciada->pbBusError = TRUE;
    else
                InsertInIrqQueues(pciada, vector); // insert at top of the queues

        return TRUE;
}

//------------------------------------------------------------------------
//  translate interrupt resources for PCIADAs to neutral ones
//
/*
NTSTATUS PCIVMETranslateInterrupts(PDEVICE_OBJECT device_Obj)
{
        int              i;
        NTSTATUS         result = STATUS_SUCCESS;
        DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
        int                              nPCIADAs = pDevExt->nPCIADAs;
        PCIADA           *pciada  = (PCIADA *) NULL;

    KdPrint(("TranslateInterrupt()\n"));
        if (nPCIADAs) return STATUS_SUCCESS;

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

                KdPrint(("In  - Bus:%d, IrqLine:%d\n", pciada->Bus, pciada->Irql));

                if (pciada->Irql)
                {
                        pciada->Vector = HalGetInterruptVector(PCIBus, pciada->Bus,
                                pciada->Irql,
                                        pciada->Vector,
                                                &pciada->Irql, &pciada->Affinity);
                }
                else
                        result = STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT;
        }

        KdPrint(("Out - Irql:%d, Vector: %d, Affinity:%d\n", pciada->Irql, pciada->Vector, pciada->Affinity));

        return result;
}
*/


//------------------------------------------------------------------------
//  connect service routines to all PCIADA interrupts
//
NTSTATUS PCIVMEConnectInterrupt(PDEVICE_OBJECT device_Obj)
{
        int              i;
        NTSTATUS         result = STATUS_SUCCESS;
        DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
        int                              nPCIADAs = pDevExt->nPCIADAs;
        PCIADA           *pciada;

    KdPrint(("ConnectInterrupt()\n"));

        // connect the interrupts to service routines
        for (i = 0; i < nPCIADAs; i++)
        {
      pciada = &pDevExt->pciada[i];

          pciada->InterruptObject = NULL;
         
          if (pciada->Vector)
                result = IoConnectInterrupt(&pciada->InterruptObject,
                        irq_service,
                                (PVOID)pciada,
                                        NULL,
                                                pciada->Vector,
                                                        pciada->Irql,
                                                                pciada->Irql,
                                                                        LevelSensitive,
                                                                                TRUE,
                                                                                        pciada->Affinity,
                                                                                                FALSE);

          KdPrint(("pIrqObj:0x%08x, VirtVect:%d, Irql:%d, hIrql:%d, Aff:0x%08x, Status:0x%08x\n",
          pciada->InterruptObject,
                                pciada->Vector, pciada->Irql, pciada->Irql, pciada->Affinity, result));

          if (result != STATUS_SUCCESS) break;
        }

        return result;
}

//------------------------------------------------------------------------
//  dis-connect service routines to all PCIADA interrupts
//
NTSTATUS PCIVMEDisConnectInterrupt(PDEVICE_OBJECT device_Obj)
{
        int              i;
        DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
        int                              nPCIADAs = pDevExt->nPCIADAs;
        PCIADA           *pciada;

    KdPrint(("DisConnectInterrupt()\n"));

        // dis connect the interrupts to service routines
        for (i = 0; i < nPCIADAs; i++)
        {
                pciada = &pDevExt->pciada[i];

                KdPrint(("IrqObj:0x%08x\n", pciada->InterruptObject));

                if (pciada->InterruptObject)
                  IoDisconnectInterrupt(pciada->InterruptObject);
        }

        return STATUS_SUCCESS;
}