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