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 NTAPI
KiChainedDispatch2ndLvl(VOID
);
24 /* PRIVATE FUNCTIONS *********************************************************/
28 KeDisableInterrupts(VOID
)
33 /* Get EFLAGS and check if the interrupt bit is set */
34 Flags
= __readeflags();
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
= MAXULONG
;
198 Interrupt
->DispatchCount
= MAXULONG
;
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 Interrupt
->Connected
= Connected
= TRUE
;
287 // ASSERT(Irql <= SYNCH_LEVEL);
289 /* Check if this is the first chain */
290 if (Dispatch
.Type
!= ChainConnect
)
292 /* This is not supported */
293 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
295 /* Setup the chainned handler */
296 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
299 /* Insert into the interrupt list */
300 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
301 &Interrupt
->InterruptListEntry
);
305 /* Unlock the dispatcher and revert affinity */
306 KiReleaseDispatcherLock(OldIrql
);
307 KeRevertToUserAffinityThread();
309 /* Check if we failed while trying to connect */
310 if ((Connected
) && (Error
))
312 DPRINT1("HalEnableSystemInterrupt failed\n");
313 KeDisconnectInterrupt(Interrupt
);
317 /* Return to caller */
326 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
330 DISPATCH_INFO Dispatch
;
331 PKINTERRUPT NextInterrupt
;
334 /* Set the affinity */
335 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
337 /* Lock the dispatcher */
338 OldIrql
= KiAcquireDispatcherLock();
340 /* Check if it's actually connected */
341 State
= Interrupt
->Connected
;
344 /* Get the vector and IRQL */
345 Irql
= Interrupt
->Irql
;
346 Vector
= Interrupt
->Vector
;
348 /* Get vector dispatch data */
349 KiGetVectorDispatch(Vector
, &Dispatch
);
351 /* Check if it was chained */
352 if (Dispatch
.Type
== ChainConnect
)
354 /* Check if the top-level interrupt is being removed */
355 ASSERT(Irql
<= SYNCH_LEVEL
);
356 if (Interrupt
== Dispatch
.Interrupt
)
358 /* Get the next one */
359 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
360 InterruptListEntry
.Flink
,
365 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
369 RemoveEntryList(&Interrupt
->InterruptListEntry
);
371 /* Get the next one */
372 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
373 InterruptListEntry
.Flink
,
377 /* Check if this is the only one left */
378 if (Dispatch
.Interrupt
== NextInterrupt
)
380 /* Connect it in non-chained mode */
381 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
386 /* Only one left, disable and remove it */
387 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
388 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
392 Interrupt
->Connected
= FALSE
;
395 /* Unlock the dispatcher and revert affinity */
396 KiReleaseDispatcherLock(OldIrql
);
397 KeRevertToUserAffinityThread();
399 /* Return to caller */