Fixed timer code
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.5 2001/02/06 00:11:19 dwelch Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/i386/irq.c
6 * PURPOSE: IRQ handling
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * UPDATE HISTORY:
9 * 29/05/98: Created
10 */
11
12 /*
13 * NOTE: In general the PIC interrupt priority facilities are used to
14 * preserve the NT IRQL semantics, global interrupt disables are only used
15 * to keep the PIC in a consistent state
16 *
17 */
18
19 /* INCLUDES ****************************************************************/
20
21 #include <ddk/ntddk.h>
22
23 #include <internal/ke.h>
24 #include <internal/ps.h>
25 #include <internal/i386/segment.h>
26
27 #define NDEBUG
28 #include <internal/debug.h>
29
30 /* GLOBALS *****************************************************************/
31
32 #define NR_IRQS (16)
33 #define IRQ_BASE (0x40)
34
35 void irq_handler_0(void);
36 void irq_handler_1(void);
37 void irq_handler_2(void);
38 void irq_handler_3(void);
39 void irq_handler_4(void);
40 void irq_handler_5(void);
41 void irq_handler_6(void);
42 void irq_handler_7(void);
43 void irq_handler_8(void);
44 void irq_handler_9(void);
45 void irq_handler_10(void);
46 void irq_handler_11(void);
47 void irq_handler_12(void);
48 void irq_handler_13(void);
49 void irq_handler_14(void);
50 void irq_handler_15(void);
51
52 static unsigned int irq_handler[NR_IRQS]=
53 {
54 (int)&irq_handler_0,
55 (int)&irq_handler_1,
56 (int)&irq_handler_2,
57 (int)&irq_handler_3,
58 (int)&irq_handler_4,
59 (int)&irq_handler_5,
60 (int)&irq_handler_6,
61 (int)&irq_handler_7,
62 (int)&irq_handler_8,
63 (int)&irq_handler_9,
64 (int)&irq_handler_10,
65 (int)&irq_handler_11,
66 (int)&irq_handler_12,
67 (int)&irq_handler_13,
68 (int)&irq_handler_14,
69 (int)&irq_handler_15,
70 };
71
72 /*
73 * PURPOSE: Object describing each isr
74 * NOTE: The data in this table is only modified at passsive level but can
75 * be accessed at any irq level.
76 */
77
78 static LIST_ENTRY isr_table[NR_IRQS]={{NULL,NULL},};
79 static PKSPIN_LOCK isr_lock[NR_IRQS] = {NULL,};
80 static KSPIN_LOCK isr_table_lock = {0,};
81
82
83 /* FUNCTIONS ****************************************************************/
84
85 #define PRESENT (0x8000)
86 #define I486_INTERRUPT_GATE (0xe00)
87
88 VOID KeInitInterrupts (VOID)
89 {
90 int i;
91
92 DPRINT("KeInitInterrupts ()\n",0);
93
94 /*
95 * Setup the IDT entries to point to the interrupt handlers
96 */
97 for (i=0;i<NR_IRQS;i++)
98 {
99 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
100 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
101 I486_INTERRUPT_GATE;
102 InitializeListHead(&isr_table[i]);
103 }
104 }
105
106 typedef struct _KIRQ_TRAPFRAME
107 {
108 ULONG Magic;
109 ULONG Fs;
110 ULONG Es;
111 ULONG Ds;
112 ULONG Eax;
113 ULONG Ecx;
114 ULONG Edx;
115 ULONG Ebx;
116 ULONG Esp;
117 ULONG Ebp;
118 ULONG Esi;
119 ULONG Edi;
120 ULONG Eip;
121 ULONG Cs;
122 ULONG Eflags;
123 } KIRQ_TRAPFRAME, *PKIRQ_TRAPFRAME;
124
125 VOID
126 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
127 /*
128 * FUNCTION: Calls the irq specific handler for an irq
129 * ARGUMENTS:
130 * irq = IRQ that has interrupted
131 */
132 {
133 KIRQL old_level;
134 PKINTERRUPT isr;
135 PLIST_ENTRY current;
136
137 // DbgPrint("{");
138
139 /*
140 * Notify the rest of the kernel of the raised irq level
141 */
142 HalBeginSystemInterrupt (irq+IRQ_BASE,
143 HIGH_LEVEL-irq,
144 &old_level);
145
146 /*
147 * Enable interrupts
148 * NOTE: Only higher priority interrupts will get through
149 */
150 __asm__("sti\n\t");
151
152 if (irq==0)
153 {
154 KiUpdateSystemTime();
155 }
156 else
157 {
158 DPRINT("KiInterruptDispatch(irq %d)\n",irq);
159 /*
160 * Iterate the list until one of the isr tells us its device interrupted
161 */
162 current = isr_table[irq].Flink;
163 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
164 DPRINT("current %x isr %x\n",current,isr);
165 while (current!=(&isr_table[irq]) &&
166 !isr->ServiceRoutine(isr,isr->ServiceContext))
167 {
168 current = current->Flink;
169 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
170 DPRINT("current %x isr %x\n",current,isr);
171 }
172 }
173
174 /*
175 * Disable interrupts
176 */
177 __asm__("cli\n\t");
178
179 /*
180 * Unmask the related irq
181 */
182 HalEnableSystemInterrupt (irq + IRQ_BASE, 0, 0);
183
184 /*
185 * If the processor level will drop below dispatch level on return then
186 * issue a DPC queue drain interrupt
187 */
188 if (old_level < DISPATCH_LEVEL)
189 {
190 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
191 __asm__("sti\n\t");
192
193 if (KeGetCurrentThread() != NULL)
194 {
195 KeGetCurrentThread()->LastEip = Trapframe->Eip;
196 }
197 KiDispatchInterrupt();
198 if (irq == 0)
199 {
200 PsDispatchThread(THREAD_STATE_RUNNABLE);
201 }
202 if (KeGetCurrentThread() != NULL &&
203 KeGetCurrentThread()->Alerted[1] != 0 &&
204 Trapframe->Cs != KERNEL_CS)
205 {
206 HalEndSystemInterrupt (APC_LEVEL, 0);
207 KiDeliverNormalApc();
208 }
209 }
210
211 HalEndSystemInterrupt (old_level, 0);
212 }
213
214
215 static VOID
216 KeDumpIrqList(VOID)
217 {
218 PKINTERRUPT current;
219 PLIST_ENTRY current_entry;
220 unsigned int i;
221
222 for (i=0;i<NR_IRQS;i++)
223 {
224 DPRINT("For irq %x ",i);
225 current_entry = isr_table[i].Flink;
226 current = CONTAINING_RECORD(current,KINTERRUPT,Entry);
227 while (current_entry!=(&isr_table[i]))
228 {
229 DPRINT("Isr %x ",current);
230 current_entry = current_entry->Flink;
231 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
232 }
233 DPRINT("\n",0);
234 }
235 }
236
237
238 NTSTATUS STDCALL
239 KeConnectInterrupt(PKINTERRUPT InterruptObject)
240 {
241 KIRQL oldlvl;
242 KIRQL synch_oldlvl;
243 PKINTERRUPT ListHead;
244 ULONG Vector;
245
246 DPRINT("KeConnectInterrupt()\n");
247
248 Vector = InterruptObject->Vector;
249
250 /*
251 * Acquire the table spinlock
252 */
253 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
254
255 /*
256 * Check if the vector is already in use that we can share it
257 */
258 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
259 if (!IsListEmpty(&isr_table[Vector]) &&
260 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
261 {
262 KeReleaseSpinLock(&isr_table_lock,oldlvl);
263 return(STATUS_INVALID_PARAMETER);
264 }
265 else
266 {
267 isr_lock[Vector]=ExAllocatePool(NonPagedPool,sizeof(KSPIN_LOCK));
268 KeInitializeSpinLock(isr_lock[Vector]);
269 }
270
271 InterruptObject->IrqLock = isr_lock[Vector];
272
273 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
274 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
275 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
276 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
277 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
278 InterruptObject->Entry.Blink);
279 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
280 KeLowerIrql(synch_oldlvl);
281
282 /*
283 * Release the table spinlock
284 */
285 KeReleaseSpinLock(&isr_table_lock,oldlvl);
286
287 KeDumpIrqList();
288
289 return STATUS_SUCCESS;
290 }
291
292
293 VOID STDCALL
294 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
295 /*
296 * FUNCTION: Releases a drivers isr
297 * ARGUMENTS:
298 * InterruptObject = isr to release
299 */
300 {
301 KIRQL oldlvl;
302
303 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
304 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
305 RemoveEntryList(&InterruptObject->Entry);
306 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
307 KeLowerIrql(oldlvl);
308 }
309
310
311 NTSTATUS
312 STDCALL
313 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
314 PKSERVICE_ROUTINE ServiceRoutine,
315 PVOID ServiceContext,
316 PKSPIN_LOCK SpinLock,
317 ULONG Vector,
318 KIRQL Irql,
319 KIRQL SynchronizeIrql,
320 KINTERRUPT_MODE InterruptMode,
321 BOOLEAN ShareVector,
322 KAFFINITY ProcessorEnableMask,
323 BOOLEAN FloatingSave)
324 {
325 InterruptObject->ServiceContext = ServiceContext;
326 InterruptObject->ServiceRoutine = ServiceRoutine;
327 InterruptObject->Vector = Vector;
328 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
329 InterruptObject->SynchLevel = SynchronizeIrql;
330 InterruptObject->Shareable = ShareVector;
331 InterruptObject->FloatingSave = FALSE;
332
333 return STATUS_SUCCESS;
334 }
335
336
337 NTSTATUS
338 STDCALL
339 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
340 PKSERVICE_ROUTINE ServiceRoutine,
341 PVOID ServiceContext,
342 PKSPIN_LOCK SpinLock,
343 ULONG Vector,
344 KIRQL Irql,
345 KIRQL SynchronizeIrql,
346 KINTERRUPT_MODE InterruptMode,
347 BOOLEAN ShareVector,
348 KAFFINITY ProcessorEnableMask,
349 BOOLEAN FloatingSave)
350 /*
351 * FUNCTION: Registers a driver's isr to be called when its device interrupts
352 * ARGUMENTS:
353 * InterruptObject (OUT) = Points to the interrupt object created on
354 * return
355 * ServiceRoutine = Routine to be called when the device interrupts
356 * ServiceContext = Parameter to be passed to ServiceRoutine
357 * SpinLock = Initalized spinlock that will be used to synchronize
358 * access between the isr and other driver routines. This is
359 * required if the isr handles more than one vector or the
360 * driver has more than one isr
361 * Vector = Interrupt vector to allocate
362 * (returned from HalGetInterruptVector)
363 * Irql = DIRQL returned from HalGetInterruptVector
364 * SynchronizeIrql = DIRQL at which the isr will execute. This must
365 * be the highest of all the DIRQLs returned from
366 * HalGetInterruptVector if the driver has multiple
367 * isrs
368 * InterruptMode = Specifies if the interrupt is LevelSensitive or
369 * Latched
370 * ShareVector = Specifies if the vector can be shared
371 * ProcessorEnableMask = Processors on the isr can run
372 * FloatingSave = TRUE if the floating point stack should be saved when
373 * the isr runs. Must be false for x86 drivers
374 * RETURNS: Status
375 * IRQL: PASSIVE_LEVEL
376 */
377 {
378 PKINTERRUPT Interrupt;
379 NTSTATUS Status = STATUS_SUCCESS;
380
381 ASSERT_IRQL(PASSIVE_LEVEL);
382
383 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
384
385 /*
386 * Check the parameters
387 */
388 if (Vector >= NR_IRQS)
389 {
390 return(STATUS_INVALID_PARAMETER);
391 }
392 if (FloatingSave == TRUE)
393 {
394 return(STATUS_INVALID_PARAMETER);
395 }
396
397 /*
398 * Initialize interrupt object
399 */
400 Interrupt=ExAllocatePool(NonPagedPool,sizeof(KINTERRUPT));
401 if (Interrupt==NULL)
402 {
403 return(STATUS_INSUFFICIENT_RESOURCES);
404 }
405
406 Status = KeInitializeInterrupt(Interrupt,
407 ServiceRoutine,
408 ServiceContext,
409 SpinLock,
410 Vector,
411 Irql,
412 SynchronizeIrql,
413 InterruptMode,
414 ShareVector,
415 ProcessorEnableMask,
416 FloatingSave);
417 if (!NT_SUCCESS(Status))
418 {
419 ExFreePool(Interrupt);
420 return Status;
421 }
422
423 Status = KeConnectInterrupt(Interrupt);
424 if (!NT_SUCCESS(Status))
425 {
426 ExFreePool(Interrupt);
427 return Status;
428 }
429
430 *InterruptObject = Interrupt;
431
432 return(STATUS_SUCCESS);
433 }
434
435
436 VOID STDCALL
437 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
438 /*
439 * FUNCTION: Releases a drivers isr
440 * ARGUMENTS:
441 * InterruptObject = isr to release
442 */
443 {
444 KeDisconnectInterrupt(InterruptObject);
445 ExFreePool(InterruptObject);
446 }
447
448 /* EOF */