- Use MAXUINT, MAXULONG, MAXDWORD, MAXULONGLONG and MAXULONGLONG instead of ~0 or...
[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 NTAPI KiChainedDispatch2ndLvl(VOID);
23
24 /* PRIVATE FUNCTIONS *********************************************************/
25
26 BOOLEAN
27 NTAPI
28 KeDisableInterrupts(VOID)
29 {
30 ULONG Flags;
31 BOOLEAN Return;
32
33 /* Get EFLAGS and check if the interrupt bit is set */
34 Flags = __readeflags();
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 = MAXULONG;
198 Interrupt->DispatchCount = MAXULONG;
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 Interrupt->Connected = Connected = TRUE;
285
286 /* FIXME */
287 // ASSERT(Irql <= SYNCH_LEVEL);
288
289 /* Check if this is the first chain */
290 if (Dispatch.Type != ChainConnect)
291 {
292 /* This is not supported */
293 ASSERT(Dispatch.Interrupt->Mode != Latched);
294
295 /* Setup the chainned handler */
296 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
297 }
298
299 /* Insert into the interrupt list */
300 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
301 &Interrupt->InterruptListEntry);
302 }
303 }
304
305 /* Unlock the dispatcher and revert affinity */
306 KiReleaseDispatcherLock(OldIrql);
307 KeRevertToUserAffinityThread();
308
309 /* Check if we failed while trying to connect */
310 if ((Connected) && (Error))
311 {
312 DPRINT1("HalEnableSystemInterrupt failed\n");
313 KeDisconnectInterrupt(Interrupt);
314 Connected = FALSE;
315 }
316
317 /* Return to caller */
318 return Connected;
319 }
320
321 /*
322 * @implemented
323 */
324 BOOLEAN
325 NTAPI
326 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
327 {
328 KIRQL OldIrql, Irql;
329 ULONG Vector;
330 DISPATCH_INFO Dispatch;
331 PKINTERRUPT NextInterrupt;
332 BOOLEAN State;
333
334 /* Set the affinity */
335 KeSetSystemAffinityThread(1 << Interrupt->Number);
336
337 /* Lock the dispatcher */
338 OldIrql = KiAcquireDispatcherLock();
339
340 /* Check if it's actually connected */
341 State = Interrupt->Connected;
342 if (State)
343 {
344 /* Get the vector and IRQL */
345 Irql = Interrupt->Irql;
346 Vector = Interrupt->Vector;
347
348 /* Get vector dispatch data */
349 KiGetVectorDispatch(Vector, &Dispatch);
350
351 /* Check if it was chained */
352 if (Dispatch.Type == ChainConnect)
353 {
354 /* Check if the top-level interrupt is being removed */
355 ASSERT(Irql <= SYNCH_LEVEL);
356 if (Interrupt == Dispatch.Interrupt)
357 {
358 /* Get the next one */
359 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
360 InterruptListEntry.Flink,
361 KINTERRUPT,
362 InterruptListEntry);
363
364 /* Reconnect it */
365 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
366 }
367
368 /* Remove it */
369 RemoveEntryList(&Interrupt->InterruptListEntry);
370
371 /* Get the next one */
372 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
373 InterruptListEntry.Flink,
374 KINTERRUPT,
375 InterruptListEntry);
376
377 /* Check if this is the only one left */
378 if (Dispatch.Interrupt == NextInterrupt)
379 {
380 /* Connect it in non-chained mode */
381 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
382 }
383 }
384 else
385 {
386 /* Only one left, disable and remove it */
387 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
388 KiConnectVectorToInterrupt(Interrupt, NoConnect);
389 }
390
391 /* Disconnect it */
392 Interrupt->Connected = FALSE;
393 }
394
395 /* Unlock the dispatcher and revert affinity */
396 KiReleaseDispatcherLock(OldIrql);
397 KeRevertToUserAffinityThread();
398
399 /* Return to caller */
400 return State;
401 }
402
403 /* EOF */