Bring back ext2 code from branch
[reactos.git] / reactos / ntoskrnl / ke / i386 / irqobj.c
1 /*
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
8 * routines.
9 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 ULONG KiISRTimeout = 55;
21 USHORT KiISROverflow = 30000;
22 extern ULONG KiChainedDispatch2ndLvl;
23
24 /* PRIVATE FUNCTIONS *********************************************************/
25
26 BOOLEAN
27 NTAPI
28 KeDisableInterrupts(VOID)
29 {
30 ULONG Flags = 0;
31 BOOLEAN Return;
32
33 /* Get EFLAGS and check if the interrupt bit is set */
34 Ke386SaveFlags(Flags);
35 Return = (Flags & EFLAGS_INTERRUPT_MASK) ? TRUE: FALSE;
36
37 /* Disable interrupts */
38 _disable();
39 return Return;
40 }
41
42 VOID
43 NTAPI
44 KiGetVectorDispatch(IN ULONG Vector,
45 IN PDISPATCH_INFO Dispatch)
46 {
47 PKINTERRUPT_ROUTINE Handler;
48 ULONG Current;
49
50 /* Setup the unhandled dispatch */
51 Dispatch->NoDispatch = (PVOID)(((ULONG_PTR)&KiStartUnexpectedRange) +
52 (Vector - PRIMARY_VECTOR_BASE) *
53 KiUnexpectedEntrySize);
54
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;
60
61 /* Get the current handler */
62 Current = ((((PKIPCR)KeGetPcr())->IDT[Vector].ExtendedOffset << 16)
63 & 0xFFFF0000) |
64 (((PKIPCR)KeGetPcr())->IDT[Vector].Offset & 0xFFFF);
65
66 /* Set the interrupt */
67 Dispatch->Interrupt = CONTAINING_RECORD(Current,
68 KINTERRUPT,
69 DispatchCode);
70
71 /* Check what this interrupt is connected to */
72 if ((PKINTERRUPT_ROUTINE)Current == Dispatch->NoDispatch)
73 {
74 /* Not connected */
75 Dispatch->Type = NoConnect;
76 }
77 else
78 {
79 /* Get the handler */
80 Handler = Dispatch->Interrupt->DispatchAddress;
81 if (Handler == Dispatch->ChainedDispatch)
82 {
83 /* It's a chained interrupt */
84 Dispatch->Type = ChainConnect;
85 }
86 else if ((Handler == Dispatch->InterruptDispatch) ||
87 (Handler == Dispatch->FloatingDispatch))
88 {
89 /* It's unchained */
90 Dispatch->Type = NormalConnect;
91 }
92 else
93 {
94 /* Unknown */
95 Dispatch->Type = UnknownConnect;
96 }
97 }
98 }
99
100 VOID
101 NTAPI
102 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt,
103 IN CONNECT_TYPE Type)
104 {
105 DISPATCH_INFO Dispatch;
106 PKINTERRUPT_ROUTINE Handler;
107 PULONG Patch = &Interrupt->DispatchCode[0];
108
109 /* Get vector data */
110 KiGetVectorDispatch(Interrupt->Vector, &Dispatch);
111
112 /* Check if we're only disconnecting */
113 if (Type == NoConnect)
114 {
115 /* Set the handler to NoDispatch */
116 Handler = Dispatch.NoDispatch;
117 }
118 else
119 {
120 /* Get the right handler */
121 Handler = (Type == NormalConnect) ?
122 Dispatch.InterruptDispatch:
123 Dispatch.ChainedDispatch;
124 ASSERT(Interrupt->FloatingSave == FALSE);
125
126 /* Set the handler */
127 Interrupt->DispatchAddress = Handler;
128
129 /* Jump to the last 4 bytes */
130 Patch = (PULONG)((ULONG_PTR)Patch +
131 ((ULONG_PTR)&KiInterruptTemplateDispatch -
132 (ULONG_PTR)KiInterruptTemplate) - 4);
133
134 /* Apply the patch */
135 *Patch = (ULONG)((ULONG_PTR)Handler - ((ULONG_PTR)Patch + 4));
136
137 /* Now set the final handler address */
138 ASSERT(Dispatch.FlatDispatch == NULL);
139 Handler = (PVOID)&Interrupt->DispatchCode;
140 }
141
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);
147 }
148
149 /* PUBLIC FUNCTIONS **********************************************************/
150
151 /*
152 * @implemented
153 */
154 VOID
155 NTAPI
156 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
157 IN PKSERVICE_ROUTINE ServiceRoutine,
158 IN PVOID ServiceContext,
159 IN PKSPIN_LOCK SpinLock,
160 IN ULONG Vector,
161 IN KIRQL Irql,
162 IN KIRQL SynchronizeIrql,
163 IN KINTERRUPT_MODE InterruptMode,
164 IN BOOLEAN ShareVector,
165 IN CHAR ProcessorNumber,
166 IN BOOLEAN FloatingSave)
167 {
168 ULONG i;
169 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
170
171 /* Set the Interrupt Header */
172 Interrupt->Type = InterruptObject;
173 Interrupt->Size = sizeof(KINTERRUPT);
174
175 /* Check if we got a spinlock */
176 if (SpinLock)
177 {
178 Interrupt->ActualLock = SpinLock;
179 }
180 else
181 {
182 /* This means we'll be usin the built-in one */
183 KeInitializeSpinLock(&Interrupt->SpinLock);
184 Interrupt->ActualLock = &Interrupt->SpinLock;
185 }
186
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;
199
200 /* Loop the template in memory */
201 for (i = 0; i < KINTERRUPT_DISPATCH_CODES; i++)
202 {
203 /* Copy the dispatch code */
204 *DispatchCode++ = KiInterruptTemplate[i];
205 }
206
207 /* Sanity check */
208 ASSERT((ULONG_PTR)&KiChainedDispatch2ndLvl -
209 (ULONG_PTR)KiInterruptTemplate <= (KINTERRUPT_DISPATCH_CODES * 4));
210
211 /* Jump to the last 4 bytes */
212 Patch = (PULONG)((ULONG_PTR)Patch +
213 ((ULONG_PTR)&KiInterruptTemplateObject -
214 (ULONG_PTR)KiInterruptTemplate) - 4);
215
216 /* Apply the patch */
217 *Patch = PtrToUlong(Interrupt);
218
219 /* Disconnect it at first */
220 Interrupt->Connected = FALSE;
221 }
222
223 /*
224 * @implemented
225 */
226 BOOLEAN
227 NTAPI
228 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
229 {
230 BOOLEAN Connected, Error, Status;
231 KIRQL Irql, OldIrql;
232 UCHAR Number;
233 ULONG Vector;
234 DISPATCH_INFO Dispatch;
235
236 /* Get data from interrupt */
237 Number = Interrupt->Number;
238 Vector = Interrupt->Vector;
239 Irql = Interrupt->Irql;
240
241 /* Validate the settings */
242 if ((Irql > HIGH_LEVEL) ||
243 (Number >= KeNumberProcessors) ||
244 (Interrupt->SynchronizeIrql < Irql) ||
245 (Interrupt->FloatingSave))
246 {
247 return FALSE;
248 }
249
250 /* Set defaults */
251 Connected = FALSE;
252 Error = FALSE;
253
254 /* Set the system affinity and acquire the dispatcher lock */
255 KeSetSystemAffinityThread(1 << Number);
256 OldIrql = KiAcquireDispatcherLock();
257
258 /* Check if it's already been connected */
259 if (!Interrupt->Connected)
260 {
261 /* Get vector dispatching information */
262 KiGetVectorDispatch(Vector, &Dispatch);
263
264 /* Check if the vector is already connected */
265 if (Dispatch.Type == NoConnect)
266 {
267 /* Do the connection */
268 Interrupt->Connected = Connected = TRUE;
269
270 /* Initialize the list */
271 InitializeListHead(&Interrupt->InterruptListEntry);
272
273 /* Connect and enable the interrupt */
274 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
275 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
276 if (!Status) Error = TRUE;
277 }
278 else if ((Dispatch.Type != UnknownConnect) &&
279 (Interrupt->ShareVector) &&
280 (Dispatch.Interrupt->ShareVector) &&
281 (Dispatch.Interrupt->Mode == Interrupt->Mode))
282 {
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);
287
288 /* Check if this is the first chain */
289 if (Dispatch.Type != ChainConnect)
290 {
291 /* Setup the chainned handler */
292 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
293 }
294
295 /* Insert into the interrupt list */
296 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
297 &Interrupt->InterruptListEntry);
298 }
299 }
300
301 /* Unlock the dispatcher and revert affinity */
302 KiReleaseDispatcherLock(OldIrql);
303 KeRevertToUserAffinityThread();
304
305 /* Check if we failed while trying to connect */
306 if ((Connected) && (Error))
307 {
308 DPRINT1("HalEnableSystemInterrupt failed\n");
309 KeDisconnectInterrupt(Interrupt);
310 Connected = FALSE;
311 }
312
313 /* Return to caller */
314 return Connected;
315 }
316
317 /*
318 * @implemented
319 */
320 BOOLEAN
321 NTAPI
322 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
323 {
324 KIRQL OldIrql, Irql;
325 ULONG Vector;
326 DISPATCH_INFO Dispatch;
327 PKINTERRUPT NextInterrupt;
328 BOOLEAN State;
329
330 /* Set the affinity */
331 KeSetSystemAffinityThread(1 << Interrupt->Number);
332
333 /* Lock the dispatcher */
334 OldIrql = KiAcquireDispatcherLock();
335
336 /* Check if it's actually connected */
337 State = Interrupt->Connected;
338 if (State)
339 {
340 /* Get the vector and IRQL */
341 Irql = Interrupt->Irql;
342 Vector = Interrupt->Vector;
343
344 /* Get vector dispatch data */
345 KiGetVectorDispatch(Vector, &Dispatch);
346
347 /* Check if it was chained */
348 if (Dispatch.Type == ChainConnect)
349 {
350 /* Check if the top-level interrupt is being removed */
351 ASSERT(Irql <= SYNCH_LEVEL);
352 if (Interrupt == Dispatch.Interrupt)
353 {
354 /* Get the next one */
355 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
356 InterruptListEntry.Flink,
357 KINTERRUPT,
358 InterruptListEntry);
359
360 /* Reconnect it */
361 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
362 }
363
364 /* Remove it */
365 RemoveEntryList(&Interrupt->InterruptListEntry);
366
367 /* Get the next one */
368 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
369 InterruptListEntry.Flink,
370 KINTERRUPT,
371 InterruptListEntry);
372
373 /* Check if this is the only one left */
374 if (Dispatch.Interrupt == NextInterrupt)
375 {
376 /* Connect it in non-chained mode */
377 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
378 }
379 }
380 else
381 {
382 /* Only one left, disable and remove it */
383 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
384 KiConnectVectorToInterrupt(Interrupt, NoConnect);
385 }
386
387 /* Disconnect it */
388 Interrupt->Connected = FALSE;
389 }
390
391 /* Unlock the dispatcher and revert affinity */
392 KiReleaseDispatcherLock(OldIrql);
393 KeRevertToUserAffinityThread();
394
395 /* Return to caller */
396 return State;
397 }
398
399 /* EOF */