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 purpopses 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 KiChainedDispatch2ndLvl
;
24 /* PRIVATE FUNCTIONS *********************************************************/
28 KeDisableInterrupts(VOID
)
33 /* Get EFLAGS and check if the interrupt bit is set */
34 Ke386SaveFlags(Flags
);
35 Return
= (Flags
& EFLAGS_INTERRUPT_MASK
) ? TRUE
: FALSE
;
37 /* Disable interrupts */
44 KiGetVectorDispatch(IN ULONG Vector
,
45 IN PDISPATCH_INFO Dispatch
)
47 PKINTERRUPT_ROUTINE Handler
;
50 /* Setup the unhandled dispatch */
51 Dispatch
->NoDispatch
= (PVOID
)(((ULONG_PTR
)&KiStartUnexpectedRange
) +
52 (Vector
- PRIMARY_VECTOR_BASE
) *
53 KiUnexpectedEntrySize
);
55 /* Setup the handlers */
56 Dispatch
->InterruptDispatch
= KiInterruptDispatch
;
57 Dispatch
->FloatingDispatch
= NULL
; // Floating Interrupts are not supported
58 Dispatch
->ChainedDispatch
= KiChainedDispatch
;
59 Dispatch
->FlatDispatch
= NULL
;
61 /* Get the current handler */
62 Current
= ((((PKIPCR
)KeGetPcr())->IDT
[Vector
].ExtendedOffset
<< 16)
64 (((PKIPCR
)KeGetPcr())->IDT
[Vector
].Offset
& 0xFFFF);
66 /* Set the interrupt */
67 Dispatch
->Interrupt
= CONTAINING_RECORD(Current
,
71 /* Check what this interrupt is connected to */
72 if ((PKINTERRUPT_ROUTINE
)Current
== Dispatch
->NoDispatch
)
75 Dispatch
->Type
= NoConnect
;
80 Handler
= Dispatch
->Interrupt
->DispatchAddress
;
81 if (Handler
== Dispatch
->ChainedDispatch
)
83 /* It's a chained interrupt */
84 Dispatch
->Type
= ChainConnect
;
86 else if ((Handler
== Dispatch
->InterruptDispatch
) ||
87 (Handler
== Dispatch
->FloatingDispatch
))
90 Dispatch
->Type
= NormalConnect
;
95 Dispatch
->Type
= UnknownConnect
;
102 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt
,
103 IN CONNECT_TYPE Type
)
105 DISPATCH_INFO Dispatch
;
106 PKINTERRUPT_ROUTINE Handler
;
107 PULONG Patch
= &Interrupt
->DispatchCode
[0];
109 /* Get vector data */
110 KiGetVectorDispatch(Interrupt
->Vector
, &Dispatch
);
112 /* Check if we're only disconnecting */
113 if (Type
== NoConnect
)
115 /* Set the handler to NoDispatch */
116 Handler
= Dispatch
.NoDispatch
;
120 /* Get the right handler */
121 Handler
= (Type
== NormalConnect
) ?
122 Dispatch
.InterruptDispatch
:
123 Dispatch
.ChainedDispatch
;
124 ASSERT(Interrupt
->FloatingSave
== FALSE
);
126 /* Set the handler */
127 Interrupt
->DispatchAddress
= Handler
;
129 /* Jump to the last 4 bytes */
130 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
131 ((ULONG_PTR
)&KiInterruptTemplateDispatch
-
132 (ULONG_PTR
)KiInterruptTemplate
) - 4);
134 /* Apply the patch */
135 *Patch
= (ULONG
)((ULONG_PTR
)Handler
- ((ULONG_PTR
)Patch
+ 4));
137 /* Now set the final handler address */
138 ASSERT(Dispatch
.FlatDispatch
== NULL
);
139 Handler
= (PVOID
)&Interrupt
->DispatchCode
;
142 /* Set the pointer in the IDT */
143 ((PKIPCR
)KeGetPcr())->IDT
[Interrupt
->Vector
].ExtendedOffset
=
144 (USHORT
)(((ULONG_PTR
)Handler
>> 16) & 0xFFFF);
145 ((PKIPCR
)KeGetPcr())->IDT
[Interrupt
->Vector
].Offset
=
146 (USHORT
)PtrToUlong(Handler
);
149 /* PUBLIC FUNCTIONS **********************************************************/
156 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
157 IN PKSERVICE_ROUTINE ServiceRoutine
,
158 IN PVOID ServiceContext
,
159 IN PKSPIN_LOCK SpinLock
,
162 IN KIRQL SynchronizeIrql
,
163 IN KINTERRUPT_MODE InterruptMode
,
164 IN BOOLEAN ShareVector
,
165 IN CHAR ProcessorNumber
,
166 IN BOOLEAN FloatingSave
)
169 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
171 /* Set the Interrupt Header */
172 Interrupt
->Type
= InterruptObject
;
173 Interrupt
->Size
= sizeof(KINTERRUPT
);
175 /* Check if we got a spinlock */
178 Interrupt
->ActualLock
= SpinLock
;
182 /* This means we'll be usin the built-in one */
183 KeInitializeSpinLock(&Interrupt
->SpinLock
);
184 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
187 /* Set the other settings */
188 Interrupt
->ServiceRoutine
= ServiceRoutine
;
189 Interrupt
->ServiceContext
= ServiceContext
;
190 Interrupt
->Vector
= Vector
;
191 Interrupt
->Irql
= Irql
;
192 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
193 Interrupt
->Mode
= InterruptMode
;
194 Interrupt
->ShareVector
= ShareVector
;
195 Interrupt
->Number
= ProcessorNumber
;
196 Interrupt
->FloatingSave
= FloatingSave
;
197 Interrupt
->TickCount
= (ULONG
)-1;
198 Interrupt
->DispatchCount
= (ULONG
)-1;
200 /* Loop the template in memory */
201 for (i
= 0; i
< KINTERRUPT_DISPATCH_CODES
; i
++)
203 /* Copy the dispatch code */
204 *DispatchCode
++ = KiInterruptTemplate
[i
];
208 ASSERT((ULONG_PTR
)&KiChainedDispatch2ndLvl
-
209 (ULONG_PTR
)KiInterruptTemplate
<= (KINTERRUPT_DISPATCH_CODES
* 4));
211 /* Jump to the last 4 bytes */
212 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
213 ((ULONG_PTR
)&KiInterruptTemplateObject
-
214 (ULONG_PTR
)KiInterruptTemplate
) - 4);
216 /* Apply the patch */
217 *Patch
= PtrToUlong(Interrupt
);
219 /* Disconnect it at first */
220 Interrupt
->Connected
= FALSE
;
228 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
230 BOOLEAN Connected
, Error
, Status
;
234 DISPATCH_INFO Dispatch
;
236 /* Get data from interrupt */
237 Number
= Interrupt
->Number
;
238 Vector
= Interrupt
->Vector
;
239 Irql
= Interrupt
->Irql
;
241 /* Validate the settings */
242 if ((Irql
> HIGH_LEVEL
) ||
243 (Number
>= KeNumberProcessors
) ||
244 (Interrupt
->SynchronizeIrql
< Irql
) ||
245 (Interrupt
->FloatingSave
))
254 /* Set the system affinity and acquire the dispatcher lock */
255 KeSetSystemAffinityThread(1 << Number
);
256 OldIrql
= KiAcquireDispatcherLock();
258 /* Check if it's already been connected */
259 if (!Interrupt
->Connected
)
261 /* Get vector dispatching information */
262 KiGetVectorDispatch(Vector
, &Dispatch
);
264 /* Check if the vector is already connected */
265 if (Dispatch
.Type
== NoConnect
)
267 /* Do the connection */
268 Interrupt
->Connected
= Connected
= TRUE
;
270 /* Initialize the list */
271 InitializeListHead(&Interrupt
->InterruptListEntry
);
273 /* Connect and enable the interrupt */
274 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
275 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
276 if (!Status
) Error
= TRUE
;
278 else if ((Dispatch
.Type
!= UnknownConnect
) &&
279 (Interrupt
->ShareVector
) &&
280 (Dispatch
.Interrupt
->ShareVector
) &&
281 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
283 /* The vector is shared and the interrupts are compatible */
284 while (TRUE
); // FIXME: NOT YET SUPPORTED/TESTED
285 Interrupt
->Connected
= Connected
= TRUE
;
286 ASSERT(Irql
<= SYNCH_LEVEL
);
288 /* Check if this is the first chain */
289 if (Dispatch
.Type
!= ChainConnect
)
291 /* Setup the chainned handler */
292 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
295 /* Insert into the interrupt list */
296 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
297 &Interrupt
->InterruptListEntry
);
301 /* Unlock the dispatcher and revert affinity */
302 KiReleaseDispatcherLock(OldIrql
);
303 KeRevertToUserAffinityThread();
305 /* Check if we failed while trying to connect */
306 if ((Connected
) && (Error
))
308 DPRINT1("HalEnableSystemInterrupt failed\n");
309 KeDisconnectInterrupt(Interrupt
);
313 /* Return to caller */
322 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
326 DISPATCH_INFO Dispatch
;
327 PKINTERRUPT NextInterrupt
;
330 /* Set the affinity */
331 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
333 /* Lock the dispatcher */
334 OldIrql
= KiAcquireDispatcherLock();
336 /* Check if it's actually connected */
337 State
= Interrupt
->Connected
;
340 /* Get the vector and IRQL */
341 Irql
= Interrupt
->Irql
;
342 Vector
= Interrupt
->Vector
;
344 /* Get vector dispatch data */
345 KiGetVectorDispatch(Vector
, &Dispatch
);
347 /* Check if it was chained */
348 if (Dispatch
.Type
== ChainConnect
)
350 /* Check if the top-level interrupt is being removed */
351 ASSERT(Irql
<= SYNCH_LEVEL
);
352 if (Interrupt
== Dispatch
.Interrupt
)
354 /* Get the next one */
355 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
356 InterruptListEntry
.Flink
,
361 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
365 RemoveEntryList(&Interrupt
->InterruptListEntry
);
367 /* Get the next one */
368 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
369 InterruptListEntry
.Flink
,
373 /* Check if this is the only one left */
374 if (Dispatch
.Interrupt
== NextInterrupt
)
376 /* Connect it in non-chained mode */
377 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
382 /* Only one left, disable and remove it */
383 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
384 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
388 Interrupt
->Connected
= FALSE
;
391 /* Unlock the dispatcher and revert affinity */
392 KiReleaseDispatcherLock(OldIrql
);
393 KeRevertToUserAffinityThread();
395 /* Return to caller */