Subversion Repositories f9daq

Rev

Rev 41 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. //-------------------------------------------------------------------------
  2. // WINNT driver for PCIVME interface from ARW Elektronik, Germany ---------
  3. // all around irq handling
  4. //
  5. // (c) 1999-2004 ARW Elektronik
  6. //
  7. // this source code is published under GPL (Open Source). You can use, redistrubute and
  8. // modify it unless this header   is not modified or deleted. No warranty is given that
  9. // this software will work like expected.
  10. // This product is not authorized for use as critical component in life support systems
  11. // wihout the express written approval of ARW Elektronik Germany.
  12. //
  13. // Please announce changes and hints to ARW Elektronik
  14. //
  15. // $Log: pcivme_i.c,v $
  16. // Revision 1.3  2004/07/24 07:07:26  klaus
  17. // Update copyright to 2004
  18. //
  19. // Revision 1.2  2003/11/15 19:12:50  klaus
  20. // Update copyright to 2003
  21. //
  22. // Revision 1.1.1.1  2003/11/14 23:16:33  klaus
  23. // First put into repository
  24. //
  25. // Revision 1.3  2002/10/27 16:17:48  klaus
  26. // Typing bug fixed caused at log addition
  27. //
  28. // Revision 1.2  2002/10/27 16:11:02  klaus
  29. // Added CVS log into header
  30. //
  31. // what                                            who          when
  32. // started                                         AR           01.08.1999
  33. // first release 1.0                                                       AR                   17.10.1999
  34. // IoConnectInterrupt, share vector now true       AR           04.03.2000
  35. // changed resource allocation caused by WIN2000   AR           08.06.2002
  36. //
  37.  
  38. //-------------------------------------------------------------------------
  39. // COMMENTS
  40. //
  41. // Each path (file_obj) maintains its own FIFO to hold incoming vector from
  42. // hardware acknowledged interrupts. These FIFOs are always accessible for read
  43. // while the path is open. In case a path enables its interrupts the link to
  44. // its FIFO is included in a list of "interrupt enabled" FIFOs. The opposite
  45. // happens when a path disables its interrupts.
  46. // There are up to MAX_PCIADA lists of "interrupt enabled" FIFOs, each list is
  47. // associated to a PCIADA (pciada).
  48. // Each time a pciada receives a interrupt request the software first checks
  49. // for a bus error interrupt. This kind of requests are directly associated with
  50. // a user initiated access and put forward to the accessor. (Excluding spurious
  51. // interrupts). If the request is no bus error request the vector is pushed
  52. // into all FIFOs queued for this PCIADA.
  53. // After this a DPC call is fired to wake up queued IRPs waiting for a vector.
  54. // The DPC call iterates through the list of FIFOs and looks if a queued IRP
  55. // exists for this path. If so the vector is pulled off the FIFO and the IRP
  56. // is released.
  57. // If a IRP arrives to request a vector the driver checks if there is a vector
  58. // stored in the path associated FIFO. If so the IRP returns immediately with
  59. // the pulled vector from the FIFO. If not the IRP is queued in a PCIADA
  60. // associated queue of pending IRPs.
  61. // When the interrupts for a path are disabled all pending vectors in the FIFO
  62. // associated with this path are still available. But if the FIFO is empty
  63. // request to get the next vector will block.
  64.  
  65. //-------------------------------------------------------------------------
  66. // INCLUDES
  67. //
  68. #include <ntddk.h>
  69. #include <pcivme_drv.h>
  70. #include <pcivme_i.h>
  71. #include <pcivme.h>
  72. #include <pciif.h>
  73. #include <pcivme_fifo.h>
  74.  
  75. //------------------------------------------------------------------------
  76. // DEFINES
  77. //
  78. #ifndef DWORD
  79. #define DWORD ULONG
  80. #endif
  81.  
  82. #ifndef WORD
  83. #define WORD USHORT
  84. #endif
  85.  
  86. //------------------------------------------------------------------------
  87. // GLOBALS
  88. //
  89.  
  90. //------------------------------------------------------------------------
  91. // FUNCTIONS
  92. //
  93.  
  94. //------------------------------------------------------------------------
  95. // determine which interrupt and evaluates vector
  96. //
  97. static UCHAR evaluate_vector(PCIADA *pciada)
  98. {
  99.         USHORT  wIntCSR = READ_REGISTER_USHORT(pciada->pwIntCSR);
  100.     USHORT  offset;
  101.         USHORT  wCntrl = READ_REGISTER_USHORT(pciada->pwCntrl);
  102.  
  103.     if (wCntrl & 0x100)   // pciada switched on ?
  104.         {
  105.                 if ((wIntCSR & 0x68) == 0x68)
  106.                 {
  107.                         // it's the pci interrupt # 2
  108.  
  109.                         // get current Cntrl - and clear interrupt
  110.                         wCntrl &= ~0x0100;  // disable
  111.                         WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);
  112.                         wCntrl |= 0x0100;   // enable again
  113.                         WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl);
  114.                         return 1;
  115.                 }
  116.  
  117.                 if ((wIntCSR & 0x45) == 0x45)
  118.                 {
  119.                         // it's the interrupt # 1
  120.                         offset = READ_REGISTER_USHORT(pciada->pwIRQStat);
  121.  
  122.                         if (offset & 1)
  123.                                 return 0xff & READ_REGISTER_UCHAR((PUCHAR)pciada->pbVector + offset);
  124.                 }
  125.         }
  126.  
  127.         return 0;
  128. }
  129.  
  130. //------------------------------------------------------------------------
  131. // enable and disable of interrupts
  132. //
  133. void globalInterruptEnable(PCIADA *pciada)
  134. {
  135.    WRITE_REGISTER_USHORT(pciada->pwIntCSR, ENABLE_PCIADA_IRQS);
  136. }
  137.  
  138. void globalInterruptDisable(PCIADA *pciada)
  139. {
  140.    WRITE_REGISTER_USHORT(pciada->pwIntCSR, DISABLE_PCIADA_IRQS);
  141. }
  142.  
  143. //------------------------------------------------------------------------
  144. // insert the vector in a queue of interrupts (and rescue waiting IRPs)
  145. //
  146. static void InsertInIrqQueues(PCIADA *pciada, UCHAR vector)
  147. {
  148.         FIFO_LIST   *next;
  149.         KIRQL           oldIrqlList;
  150.         register PLIST_ENTRY pList = &pciada->IrqListList;
  151.  
  152.         KdPrint(("InsertInIrqQueues\n"));
  153.  
  154.         // iterate all fifos and insert the vector in each fifo
  155.         KeAcquireSpinLock(&pciada->IrqListLock, &oldIrqlList);
  156.  
  157.         while (pList->Flink != &pciada->IrqListList)
  158.         {
  159.                 pList = pList->Flink;
  160.                 next = CONTAINING_RECORD(pList, FIFO_LIST, entry);
  161.  
  162.                 KdPrint(("Vector %d pushed\n", vector));
  163.  
  164.                 // push the vector into the fifo
  165.                 PushElement(next->pIrqListHandle, vector);
  166.         }
  167.          
  168.         KeReleaseSpinLock(&pciada->IrqListLock, oldIrqlList);
  169.  
  170.         // more handling in the deffered procedure call
  171.         KeInsertQueueDpc(&pciada->kDPCobj, (PVOID)pciada, (PVOID)&vector);
  172. }
  173.  
  174. //------------------------------------------------------------------------
  175. // main interrupt handler function
  176. //
  177. BOOLEAN irq_service(PKINTERRUPT Interrupt, PVOID ServiceContext)
  178. {
  179.         PCIADA *pciada = (PCIADA *)ServiceContext;
  180.         UCHAR  vector;
  181.         UNREFERENCED_PARAMETER(Interrupt);
  182.         vector = evaluate_vector(pciada);
  183.         if (!(vector)) return FALSE;
  184.    
  185.         KdPrint(("irq_service: %d\n", vector & 0xff));
  186.  
  187.         if ((pciada->pbBusError) && ((vector == 7) || (vector == 1)))
  188.                 *pciada->pbBusError = TRUE;
  189.     else
  190.                 InsertInIrqQueues(pciada, vector); // insert at top of the queues
  191.  
  192.         return TRUE;
  193. }
  194.  
  195. //------------------------------------------------------------------------
  196. //  translate interrupt resources for PCIADAs to neutral ones
  197. //
  198. /*
  199. NTSTATUS PCIVMETranslateInterrupts(PDEVICE_OBJECT device_Obj)
  200. {
  201.         int              i;
  202.         NTSTATUS         result = STATUS_SUCCESS;
  203.         DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
  204.         int                              nPCIADAs = pDevExt->nPCIADAs;
  205.         PCIADA           *pciada  = (PCIADA *) NULL;
  206.  
  207.     KdPrint(("TranslateInterrupt()\n"));
  208.         if (nPCIADAs) return STATUS_SUCCESS;
  209.  
  210.         for (i = 0; i < nPCIADAs; i++)
  211.         {
  212.                 pciada = &pDevExt->pciada[i];
  213.  
  214.                 KdPrint(("In  - Bus:%d, IrqLine:%d\n", pciada->Bus, pciada->Irql));
  215.  
  216.                 if (pciada->Irql)
  217.                 {
  218.                         pciada->Vector = HalGetInterruptVector(PCIBus, pciada->Bus,
  219.                                 pciada->Irql,
  220.                                         pciada->Vector,
  221.                                                 &pciada->Irql, &pciada->Affinity);
  222.                 }
  223.                 else
  224.                         result = STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT;
  225.         }
  226.  
  227.         KdPrint(("Out - Irql:%d, Vector: %d, Affinity:%d\n", pciada->Irql, pciada->Vector, pciada->Affinity));
  228.  
  229.         return result;
  230. }
  231. */
  232.  
  233. //------------------------------------------------------------------------
  234. //  connect service routines to all PCIADA interrupts
  235. //
  236. NTSTATUS PCIVMEConnectInterrupt(PDEVICE_OBJECT device_Obj)
  237. {
  238.         int              i;
  239.         NTSTATUS         result = STATUS_SUCCESS;
  240.         DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
  241.         int                              nPCIADAs = pDevExt->nPCIADAs;
  242.         PCIADA           *pciada;
  243.  
  244.     KdPrint(("ConnectInterrupt()\n"));
  245.  
  246.         // connect the interrupts to service routines
  247.         for (i = 0; i < nPCIADAs; i++)
  248.         {
  249.       pciada = &pDevExt->pciada[i];
  250.  
  251.           pciada->InterruptObject = NULL;
  252.          
  253.           if (pciada->Vector)
  254.                 result = IoConnectInterrupt(&pciada->InterruptObject,
  255.                         irq_service,
  256.                                 (PVOID)pciada,
  257.                                         NULL,
  258.                                                 pciada->Vector,
  259.                                                         pciada->Irql,
  260.                                                                 pciada->Irql,
  261.                                                                         LevelSensitive,
  262.                                                                                 TRUE,
  263.                                                                                         pciada->Affinity,
  264.                                                                                                 FALSE);
  265.  
  266.           KdPrint(("pIrqObj:0x%08x, VirtVect:%d, Irql:%d, hIrql:%d, Aff:0x%08x, Status:0x%08x\n",
  267.           pciada->InterruptObject,
  268.                                 pciada->Vector, pciada->Irql, pciada->Irql, pciada->Affinity, result));
  269.  
  270.           if (result != STATUS_SUCCESS) break;
  271.         }
  272.  
  273.         return result;
  274. }
  275.  
  276. //------------------------------------------------------------------------
  277. //  dis-connect service routines to all PCIADA interrupts
  278. //
  279. NTSTATUS PCIVMEDisConnectInterrupt(PDEVICE_OBJECT device_Obj)
  280. {
  281.         int              i;
  282.         DEVICE_EXT       *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
  283.         int                              nPCIADAs = pDevExt->nPCIADAs;
  284.         PCIADA           *pciada;
  285.  
  286.     KdPrint(("DisConnectInterrupt()\n"));
  287.  
  288.         // dis connect the interrupts to service routines
  289.         for (i = 0; i < nPCIADAs; i++)
  290.         {
  291.                 pciada = &pDevExt->pciada[i];
  292.  
  293.                 KdPrint(("IrqObj:0x%08x\n", pciada->InterruptObject));
  294.  
  295.                 if (pciada->InterruptObject)
  296.                   IoDisconnectInterrupt(pciada->InterruptObject);
  297.         }
  298.  
  299.         return STATUS_SUCCESS;
  300. }
  301.  
  302.  
  303.