2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/irq.c
5 * PURPOSE: Manages the Kernel's IRQ support for external drivers,
6 * for the purposes of connecting, disconnecting and setting
7 * up ISRs for drivers. The backend behind the Io* Interrupt
9 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
12 /* INCLUDES *****************************************************************/
18 /* GLOBALS *******************************************************************/
20 ULONG KiISRTimeout
= 55;
21 USHORT KiISROverflow
= 30000;
22 extern ULONG NTAPI
KiChainedDispatch2ndLvl(VOID
);
24 /* PRIVATE FUNCTIONS *********************************************************/
28 KiGetVectorDispatch(IN ULONG Vector
,
29 IN PDISPATCH_INFO Dispatch
)
31 PKINTERRUPT_ROUTINE Handler
;
36 /* Check if this is a primary or 2nd-level dispatch */
37 Type
= HalSystemVectorDispatchEntry(Vector
,
38 &Dispatch
->FlatDispatch
,
39 &Dispatch
->NoDispatch
);
42 /* Get the IDT entry for this vector */
43 Entry
= HalVectorToIDTEntry(Vector
);
45 /* Setup the unhandled dispatch */
46 Dispatch
->NoDispatch
= (PVOID
)(((ULONG_PTR
)&KiStartUnexpectedRange
) +
47 (Entry
- PRIMARY_VECTOR_BASE
) *
48 KiUnexpectedEntrySize
);
50 /* Setup the handlers */
51 Dispatch
->InterruptDispatch
= (PVOID
)KiInterruptDispatch
;
52 Dispatch
->FloatingDispatch
= NULL
; // Floating Interrupts are not supported
53 Dispatch
->ChainedDispatch
= (PVOID
)KiChainedDispatch
;
54 Dispatch
->FlatDispatch
= NULL
;
56 /* Get the current handler */
57 Current
= KeQueryInterruptHandler(Vector
);
59 /* Set the interrupt */
60 Dispatch
->Interrupt
= CONTAINING_RECORD(Current
,
64 /* Check what this interrupt is connected to */
65 if ((PKINTERRUPT_ROUTINE
)Current
== Dispatch
->NoDispatch
)
68 Dispatch
->Type
= NoConnect
;
73 Handler
= Dispatch
->Interrupt
->DispatchAddress
;
74 if (Handler
== Dispatch
->ChainedDispatch
)
76 /* It's a chained interrupt */
77 Dispatch
->Type
= ChainConnect
;
79 else if ((Handler
== Dispatch
->InterruptDispatch
) ||
80 (Handler
== Dispatch
->FloatingDispatch
))
83 Dispatch
->Type
= NormalConnect
;
88 Dispatch
->Type
= UnknownConnect
;
95 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt
,
98 DISPATCH_INFO Dispatch
;
99 PKINTERRUPT_ROUTINE Handler
;
100 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
101 PULONG Patch
= &Interrupt
->DispatchCode
[0];
104 /* Get vector data */
105 KiGetVectorDispatch(Interrupt
->Vector
, &Dispatch
);
107 /* Check if we're only disconnecting */
108 if (Type
== NoConnect
)
110 /* Set the handler to NoDispatch */
111 Handler
= Dispatch
.NoDispatch
;
115 /* Get the right handler */
116 Handler
= (Type
== NormalConnect
) ?
117 Dispatch
.InterruptDispatch
:
118 Dispatch
.ChainedDispatch
;
119 ASSERT(Interrupt
->FloatingSave
== FALSE
);
121 /* Set the handler */
122 Interrupt
->DispatchAddress
= Handler
;
124 /* Read note in trap.s -- patching not needed since JMP is static */
125 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
126 /* Jump to the last 4 bytes */
127 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
128 ((ULONG_PTR
)&KiInterruptTemplateDispatch
-
129 (ULONG_PTR
)KiInterruptTemplate
) - 4);
131 /* Apply the patch */
132 *Patch
= (ULONG
)((ULONG_PTR
)Handler
- ((ULONG_PTR
)Patch
+ 4));
135 /* Now set the final handler address */
136 ASSERT(Dispatch
.FlatDispatch
== NULL
);
137 Handler
= (PVOID
)&Interrupt
->DispatchCode
;
140 /* Register the interrupt */
141 KeRegisterInterruptHandler(Interrupt
->Vector
, Handler
);
144 /* PUBLIC FUNCTIONS **********************************************************/
151 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
152 IN PKSERVICE_ROUTINE ServiceRoutine
,
153 IN PVOID ServiceContext
,
154 IN PKSPIN_LOCK SpinLock
,
157 IN KIRQL SynchronizeIrql
,
158 IN KINTERRUPT_MODE InterruptMode
,
159 IN BOOLEAN ShareVector
,
160 IN CHAR ProcessorNumber
,
161 IN BOOLEAN FloatingSave
)
164 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
166 /* Set the Interrupt Header */
167 Interrupt
->Type
= InterruptObject
;
168 Interrupt
->Size
= sizeof(KINTERRUPT
);
170 /* Check if we got a spinlock */
173 Interrupt
->ActualLock
= SpinLock
;
177 /* This means we'll be usin the built-in one */
178 KeInitializeSpinLock(&Interrupt
->SpinLock
);
179 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
182 /* Set the other settings */
183 Interrupt
->ServiceRoutine
= ServiceRoutine
;
184 Interrupt
->ServiceContext
= ServiceContext
;
185 Interrupt
->Vector
= Vector
;
186 Interrupt
->Irql
= Irql
;
187 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
188 Interrupt
->Mode
= InterruptMode
;
189 Interrupt
->ShareVector
= ShareVector
;
190 Interrupt
->Number
= ProcessorNumber
;
191 Interrupt
->FloatingSave
= FloatingSave
;
192 Interrupt
->TickCount
= MAXULONG
;
193 Interrupt
->DispatchCount
= MAXULONG
;
195 /* Loop the template in memory */
196 for (i
= 0; i
< KINTERRUPT_DISPATCH_CODES
; i
++)
198 /* Copy the dispatch code */
199 *DispatchCode
++ = KiInterruptTemplate
[i
];
203 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
204 ASSERT((ULONG_PTR
)&KiChainedDispatch2ndLvl
-
205 (ULONG_PTR
)KiInterruptTemplate
<= (KINTERRUPT_DISPATCH_CODES
* 4));
208 /* Jump to the last 4 bytes */
209 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
210 ((ULONG_PTR
)&KiInterruptTemplateObject
-
211 (ULONG_PTR
)KiInterruptTemplate
) - 4);
213 /* Apply the patch */
214 *Patch
= PtrToUlong(Interrupt
);
216 /* Disconnect it at first */
217 Interrupt
->Connected
= FALSE
;
225 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
227 BOOLEAN Connected
, Error
, Status
;
231 DISPATCH_INFO Dispatch
;
233 /* Get data from interrupt */
234 Number
= Interrupt
->Number
;
235 Vector
= Interrupt
->Vector
;
236 Irql
= Interrupt
->Irql
;
238 /* Validate the settings */
239 if ((Irql
> HIGH_LEVEL
) ||
240 (Number
>= KeNumberProcessors
) ||
241 (Interrupt
->SynchronizeIrql
< Irql
) ||
242 (Interrupt
->FloatingSave
))
251 /* Set the system affinity and acquire the dispatcher lock */
252 KeSetSystemAffinityThread(1 << Number
);
253 OldIrql
= KiAcquireDispatcherLock();
255 /* Check if it's already been connected */
256 if (!Interrupt
->Connected
)
258 /* Get vector dispatching information */
259 KiGetVectorDispatch(Vector
, &Dispatch
);
261 /* Check if the vector is already connected */
262 if (Dispatch
.Type
== NoConnect
)
264 /* Do the connection */
265 Interrupt
->Connected
= Connected
= TRUE
;
267 /* Initialize the list */
268 InitializeListHead(&Interrupt
->InterruptListEntry
);
270 /* Connect and enable the interrupt */
271 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
272 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
273 if (!Status
) Error
= TRUE
;
275 else if ((Dispatch
.Type
!= UnknownConnect
) &&
276 (Interrupt
->ShareVector
) &&
277 (Dispatch
.Interrupt
->ShareVector
) &&
278 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
280 /* The vector is shared and the interrupts are compatible */
281 Interrupt
->Connected
= Connected
= TRUE
;
284 // ASSERT(Irql <= SYNCH_LEVEL);
286 /* Check if this is the first chain */
287 if (Dispatch
.Type
!= ChainConnect
)
289 /* This is not supported */
290 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
292 /* Setup the chainned handler */
293 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
296 /* Insert into the interrupt list */
297 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
298 &Interrupt
->InterruptListEntry
);
302 /* Unlock the dispatcher and revert affinity */
303 KiReleaseDispatcherLock(OldIrql
);
304 KeRevertToUserAffinityThread();
306 /* Check if we failed while trying to connect */
307 if ((Connected
) && (Error
))
309 DPRINT1("HalEnableSystemInterrupt failed\n");
310 KeDisconnectInterrupt(Interrupt
);
314 /* Return to caller */
323 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
327 DISPATCH_INFO Dispatch
;
328 PKINTERRUPT NextInterrupt
;
331 /* Set the affinity */
332 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
334 /* Lock the dispatcher */
335 OldIrql
= KiAcquireDispatcherLock();
337 /* Check if it's actually connected */
338 State
= Interrupt
->Connected
;
341 /* Get the vector and IRQL */
342 Irql
= Interrupt
->Irql
;
343 Vector
= Interrupt
->Vector
;
345 /* Get vector dispatch data */
346 KiGetVectorDispatch(Vector
, &Dispatch
);
348 /* Check if it was chained */
349 if (Dispatch
.Type
== ChainConnect
)
351 /* Check if the top-level interrupt is being removed */
352 ASSERT(Irql
<= SYNCH_LEVEL
);
353 if (Interrupt
== Dispatch
.Interrupt
)
355 /* Get the next one */
356 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
357 InterruptListEntry
.Flink
,
362 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
366 RemoveEntryList(&Interrupt
->InterruptListEntry
);
368 /* Get the next one */
369 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
370 InterruptListEntry
.Flink
,
374 /* Check if this is the only one left */
375 if (Dispatch
.Interrupt
== NextInterrupt
)
377 /* Connect it in non-chained mode */
378 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
383 /* Only one left, disable and remove it */
384 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
385 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
389 Interrupt
->Connected
= FALSE
;
392 /* Unlock the dispatcher and revert affinity */
393 KiReleaseDispatcherLock(OldIrql
);
394 KeRevertToUserAffinityThread();
396 /* Return to caller */
405 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt
,
406 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine
,
407 IN PVOID SynchronizeContext OPTIONAL
)
413 OldIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
415 /* Acquire interrupt spinlock */
416 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
418 /* Call the routine */
419 Status
= SynchronizeRoutine(SynchronizeContext
);
422 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
425 KfLowerIrql(OldIrql
);