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
= KiInterruptDispatch
;
52 Dispatch
->FloatingDispatch
= NULL
; // Floating Interrupts are not supported
53 Dispatch
->ChainedDispatch
= KiChainedDispatch
;
54 Dispatch
->FlatDispatch
= NULL
;
56 /* Get the current handler */
57 Current
= ((((PKIPCR
)KeGetPcr())->IDT
[Entry
].ExtendedOffset
<< 16)
59 (((PKIPCR
)KeGetPcr())->IDT
[Entry
].Offset
& 0xFFFF);
61 /* Set the interrupt */
62 Dispatch
->Interrupt
= CONTAINING_RECORD(Current
,
66 /* Check what this interrupt is connected to */
67 if ((PKINTERRUPT_ROUTINE
)Current
== Dispatch
->NoDispatch
)
70 Dispatch
->Type
= NoConnect
;
75 Handler
= Dispatch
->Interrupt
->DispatchAddress
;
76 if (Handler
== Dispatch
->ChainedDispatch
)
78 /* It's a chained interrupt */
79 Dispatch
->Type
= ChainConnect
;
81 else if ((Handler
== Dispatch
->InterruptDispatch
) ||
82 (Handler
== Dispatch
->FloatingDispatch
))
85 Dispatch
->Type
= NormalConnect
;
90 Dispatch
->Type
= UnknownConnect
;
97 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt
,
100 DISPATCH_INFO Dispatch
;
101 PKINTERRUPT_ROUTINE Handler
;
102 PULONG Patch
= &Interrupt
->DispatchCode
[0];
105 /* Get vector data */
106 KiGetVectorDispatch(Interrupt
->Vector
, &Dispatch
);
108 /* Check if we're only disconnecting */
109 if (Type
== NoConnect
)
111 /* Set the handler to NoDispatch */
112 Handler
= Dispatch
.NoDispatch
;
116 /* Get the right handler */
117 Handler
= (Type
== NormalConnect
) ?
118 Dispatch
.InterruptDispatch
:
119 Dispatch
.ChainedDispatch
;
120 ASSERT(Interrupt
->FloatingSave
== FALSE
);
122 /* Set the handler */
123 Interrupt
->DispatchAddress
= Handler
;
125 /* Jump to the last 4 bytes */
126 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
127 ((ULONG_PTR
)&KiInterruptTemplateDispatch
-
128 (ULONG_PTR
)KiInterruptTemplate
) - 4);
130 /* Apply the patch */
131 *Patch
= (ULONG
)((ULONG_PTR
)Handler
- ((ULONG_PTR
)Patch
+ 4));
133 /* Now set the final handler address */
134 ASSERT(Dispatch
.FlatDispatch
== NULL
);
135 Handler
= (PVOID
)&Interrupt
->DispatchCode
;
138 /* Get the IDT entry for this vector */
139 Entry
= HalVectorToIDTEntry(Interrupt
->Vector
);
141 /* Set the pointer in the IDT */
142 ((PKIPCR
)KeGetPcr())->IDT
[Entry
].ExtendedOffset
=
143 (USHORT
)(((ULONG_PTR
)Handler
>> 16) & 0xFFFF);
144 ((PKIPCR
)KeGetPcr())->IDT
[Entry
].Offset
=
145 (USHORT
)PtrToUlong(Handler
);
148 /* PUBLIC FUNCTIONS **********************************************************/
155 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
156 IN PKSERVICE_ROUTINE ServiceRoutine
,
157 IN PVOID ServiceContext
,
158 IN PKSPIN_LOCK SpinLock
,
161 IN KIRQL SynchronizeIrql
,
162 IN KINTERRUPT_MODE InterruptMode
,
163 IN BOOLEAN ShareVector
,
164 IN CHAR ProcessorNumber
,
165 IN BOOLEAN FloatingSave
)
168 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
170 /* Set the Interrupt Header */
171 Interrupt
->Type
= InterruptObject
;
172 Interrupt
->Size
= sizeof(KINTERRUPT
);
174 /* Check if we got a spinlock */
177 Interrupt
->ActualLock
= SpinLock
;
181 /* This means we'll be usin the built-in one */
182 KeInitializeSpinLock(&Interrupt
->SpinLock
);
183 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
186 /* Set the other settings */
187 Interrupt
->ServiceRoutine
= ServiceRoutine
;
188 Interrupt
->ServiceContext
= ServiceContext
;
189 Interrupt
->Vector
= Vector
;
190 Interrupt
->Irql
= Irql
;
191 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
192 Interrupt
->Mode
= InterruptMode
;
193 Interrupt
->ShareVector
= ShareVector
;
194 Interrupt
->Number
= ProcessorNumber
;
195 Interrupt
->FloatingSave
= FloatingSave
;
196 Interrupt
->TickCount
= MAXULONG
;
197 Interrupt
->DispatchCount
= MAXULONG
;
199 /* Loop the template in memory */
200 for (i
= 0; i
< KINTERRUPT_DISPATCH_CODES
; i
++)
202 /* Copy the dispatch code */
203 *DispatchCode
++ = KiInterruptTemplate
[i
];
207 ASSERT((ULONG_PTR
)&KiChainedDispatch2ndLvl
-
208 (ULONG_PTR
)KiInterruptTemplate
<= (KINTERRUPT_DISPATCH_CODES
* 4));
210 /* Jump to the last 4 bytes */
211 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
212 ((ULONG_PTR
)&KiInterruptTemplateObject
-
213 (ULONG_PTR
)KiInterruptTemplate
) - 4);
215 /* Apply the patch */
216 *Patch
= PtrToUlong(Interrupt
);
218 /* Disconnect it at first */
219 Interrupt
->Connected
= FALSE
;
227 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
229 BOOLEAN Connected
, Error
, Status
;
233 DISPATCH_INFO Dispatch
;
235 /* Get data from interrupt */
236 Number
= Interrupt
->Number
;
237 Vector
= Interrupt
->Vector
;
238 Irql
= Interrupt
->Irql
;
240 /* Validate the settings */
241 if ((Irql
> HIGH_LEVEL
) ||
242 (Number
>= KeNumberProcessors
) ||
243 (Interrupt
->SynchronizeIrql
< Irql
) ||
244 (Interrupt
->FloatingSave
))
253 /* Set the system affinity and acquire the dispatcher lock */
254 KeSetSystemAffinityThread(1 << Number
);
255 OldIrql
= KiAcquireDispatcherLock();
257 /* Check if it's already been connected */
258 if (!Interrupt
->Connected
)
260 /* Get vector dispatching information */
261 KiGetVectorDispatch(Vector
, &Dispatch
);
263 /* Check if the vector is already connected */
264 if (Dispatch
.Type
== NoConnect
)
266 /* Do the connection */
267 Interrupt
->Connected
= Connected
= TRUE
;
269 /* Initialize the list */
270 InitializeListHead(&Interrupt
->InterruptListEntry
);
272 /* Connect and enable the interrupt */
273 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
274 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
275 if (!Status
) Error
= TRUE
;
277 else if ((Dispatch
.Type
!= UnknownConnect
) &&
278 (Interrupt
->ShareVector
) &&
279 (Dispatch
.Interrupt
->ShareVector
) &&
280 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
282 /* The vector is shared and the interrupts are compatible */
283 Interrupt
->Connected
= Connected
= TRUE
;
286 // ASSERT(Irql <= SYNCH_LEVEL);
288 /* Check if this is the first chain */
289 if (Dispatch
.Type
!= ChainConnect
)
291 /* This is not supported */
292 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
294 /* Setup the chainned handler */
295 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
298 /* Insert into the interrupt list */
299 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
300 &Interrupt
->InterruptListEntry
);
304 /* Unlock the dispatcher and revert affinity */
305 KiReleaseDispatcherLock(OldIrql
);
306 KeRevertToUserAffinityThread();
308 /* Check if we failed while trying to connect */
309 if ((Connected
) && (Error
))
311 DPRINT1("HalEnableSystemInterrupt failed\n");
312 KeDisconnectInterrupt(Interrupt
);
316 /* Return to caller */
325 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
329 DISPATCH_INFO Dispatch
;
330 PKINTERRUPT NextInterrupt
;
333 /* Set the affinity */
334 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
336 /* Lock the dispatcher */
337 OldIrql
= KiAcquireDispatcherLock();
339 /* Check if it's actually connected */
340 State
= Interrupt
->Connected
;
343 /* Get the vector and IRQL */
344 Irql
= Interrupt
->Irql
;
345 Vector
= Interrupt
->Vector
;
347 /* Get vector dispatch data */
348 KiGetVectorDispatch(Vector
, &Dispatch
);
350 /* Check if it was chained */
351 if (Dispatch
.Type
== ChainConnect
)
353 /* Check if the top-level interrupt is being removed */
354 ASSERT(Irql
<= SYNCH_LEVEL
);
355 if (Interrupt
== Dispatch
.Interrupt
)
357 /* Get the next one */
358 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
359 InterruptListEntry
.Flink
,
364 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
368 RemoveEntryList(&Interrupt
->InterruptListEntry
);
370 /* Get the next one */
371 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
372 InterruptListEntry
.Flink
,
376 /* Check if this is the only one left */
377 if (Dispatch
.Interrupt
== NextInterrupt
)
379 /* Connect it in non-chained mode */
380 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
385 /* Only one left, disable and remove it */
386 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
387 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
391 Interrupt
->Connected
= FALSE
;
394 /* Unlock the dispatcher and revert affinity */
395 KiReleaseDispatcherLock(OldIrql
);
396 KeRevertToUserAffinityThread();
398 /* Return to caller */