Implemented hal system interrupt functions
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.2 2000/07/24 23:51:46 ekohl 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
107 VOID KiInterruptDispatch (ULONG irq)
108 /*
109 * FUNCTION: Calls the irq specific handler for an irq
110 * ARGUMENTS:
111 * irq = IRQ that has interrupted
112 */
113 {
114 KIRQL old_level;
115 PKINTERRUPT isr;
116 PLIST_ENTRY current;
117
118 // DbgPrint("{");
119
120 /*
121 * Notify the rest of the kernel of the raised irq level
122 */
123 HalBeginSystemInterrupt (irq+IRQ_BASE,
124 HIGH_LEVEL-irq,
125 &old_level);
126
127 /*
128 * Enable interrupts
129 * NOTE: Only higher priority interrupts will get through
130 */
131 __asm__("sti\n\t");
132
133 if (irq==0)
134 {
135 KiUpdateSystemTime();
136 }
137 else
138 {
139 DPRINT("KiInterruptDispatch(irq %d)\n",irq);
140 /*
141 * Iterate the list until one of the isr tells us its device interrupted
142 */
143 current = isr_table[irq].Flink;
144 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
145 DPRINT("current %x isr %x\n",current,isr);
146 while (current!=(&isr_table[irq]) &&
147 !isr->ServiceRoutine(isr,isr->ServiceContext))
148 {
149 current = current->Flink;
150 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
151 DPRINT("current %x isr %x\n",current,isr);
152 }
153 }
154
155 /*
156 * Disable interrupts
157 */
158 __asm__("cli\n\t");
159
160 /*
161 * Unmask the related irq
162 */
163 HalEnableSystemInterrupt (irq + IRQ_BASE, 0, 0);
164
165 /*
166 * If the processor level will drop below dispatch level on return then
167 * issue a DPC queue drain interrupt
168 */
169 if (old_level < DISPATCH_LEVEL)
170 {
171 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
172 __asm__("sti\n\t");
173
174 if (irq == 0)
175 {
176 KeExpireTimers();
177 }
178 KiDispatchInterrupt();
179 PsDispatchThread(THREAD_STATE_RUNNABLE);
180 }
181 else
182 {
183 // DbgPrint("$");
184 }
185
186 HalEndSystemInterrupt (old_level, 0);
187 // DbgPrint("}");
188 }
189
190
191 static VOID KeDumpIrqList(VOID)
192 {
193 PKINTERRUPT current;
194 PLIST_ENTRY current_entry;
195 unsigned int i;
196
197 for (i=0;i<NR_IRQS;i++)
198 {
199 DPRINT("For irq %x ",i);
200 current_entry = isr_table[i].Flink;
201 current = CONTAINING_RECORD(current,KINTERRUPT,Entry);
202 while (current_entry!=(&isr_table[i]))
203 {
204 DPRINT("Isr %x ",current);
205 current_entry = current_entry->Flink;
206 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
207 }
208 DPRINT("\n",0);
209 }
210 }
211
212
213 NTSTATUS
214 STDCALL
215 KeConnectInterrupt(PKINTERRUPT InterruptObject)
216 {
217 KIRQL oldlvl;
218 KIRQL synch_oldlvl;
219 PKINTERRUPT ListHead;
220 ULONG Vector;
221
222 DPRINT("KeConnectInterrupt()\n");
223
224 Vector = InterruptObject->Vector;
225
226 /*
227 * Acquire the table spinlock
228 */
229 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
230
231 /*
232 * Check if the vector is already in use that we can share it
233 */
234 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
235 if (!IsListEmpty(&isr_table[Vector]) &&
236 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
237 {
238 KeReleaseSpinLock(&isr_table_lock,oldlvl);
239 return(STATUS_INVALID_PARAMETER);
240 }
241 else
242 {
243 isr_lock[Vector]=ExAllocatePool(NonPagedPool,sizeof(KSPIN_LOCK));
244 KeInitializeSpinLock(isr_lock[Vector]);
245 }
246
247 InterruptObject->IrqLock = isr_lock[Vector];
248
249 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
250 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
251 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
252 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
253 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
254 InterruptObject->Entry.Blink);
255 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
256 KeLowerIrql(synch_oldlvl);
257
258 /*
259 * Release the table spinlock
260 */
261 KeReleaseSpinLock(&isr_table_lock,oldlvl);
262
263 KeDumpIrqList();
264
265 return STATUS_SUCCESS;
266 }
267
268
269 VOID
270 STDCALL
271 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
272 /*
273 * FUNCTION: Releases a drivers isr
274 * ARGUMENTS:
275 * InterruptObject = isr to release
276 */
277 {
278 KIRQL oldlvl;
279
280 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
281 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
282 RemoveEntryList(&InterruptObject->Entry);
283 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
284 KeLowerIrql(oldlvl);
285 }
286
287
288 NTSTATUS
289 STDCALL
290 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
291 PKSERVICE_ROUTINE ServiceRoutine,
292 PVOID ServiceContext,
293 PKSPIN_LOCK SpinLock,
294 ULONG Vector,
295 KIRQL Irql,
296 KIRQL SynchronizeIrql,
297 KINTERRUPT_MODE InterruptMode,
298 BOOLEAN ShareVector,
299 KAFFINITY ProcessorEnableMask,
300 BOOLEAN FloatingSave)
301 {
302 InterruptObject->ServiceContext = ServiceContext;
303 InterruptObject->ServiceRoutine = ServiceRoutine;
304 InterruptObject->Vector = Vector;
305 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
306 InterruptObject->SynchLevel = SynchronizeIrql;
307 InterruptObject->Shareable = ShareVector;
308 InterruptObject->FloatingSave = FALSE;
309
310 return STATUS_SUCCESS;
311 }
312
313
314 NTSTATUS
315 STDCALL
316 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
317 PKSERVICE_ROUTINE ServiceRoutine,
318 PVOID ServiceContext,
319 PKSPIN_LOCK SpinLock,
320 ULONG Vector,
321 KIRQL Irql,
322 KIRQL SynchronizeIrql,
323 KINTERRUPT_MODE InterruptMode,
324 BOOLEAN ShareVector,
325 KAFFINITY ProcessorEnableMask,
326 BOOLEAN FloatingSave)
327 /*
328 * FUNCTION: Registers a driver's isr to be called when its device interrupts
329 * ARGUMENTS:
330 * InterruptObject (OUT) = Points to the interrupt object created on
331 * return
332 * ServiceRoutine = Routine to be called when the device interrupts
333 * ServiceContext = Parameter to be passed to ServiceRoutine
334 * SpinLock = Initalized spinlock that will be used to synchronize
335 * access between the isr and other driver routines. This is
336 * required if the isr handles more than one vector or the
337 * driver has more than one isr
338 * Vector = Interrupt vector to allocate
339 * (returned from HalGetInterruptVector)
340 * Irql = DIRQL returned from HalGetInterruptVector
341 * SynchronizeIrql = DIRQL at which the isr will execute. This must
342 * be the highest of all the DIRQLs returned from
343 * HalGetInterruptVector if the driver has multiple
344 * isrs
345 * InterruptMode = Specifies if the interrupt is LevelSensitive or
346 * Latched
347 * ShareVector = Specifies if the vector can be shared
348 * ProcessorEnableMask = Processors on the isr can run
349 * FloatingSave = TRUE if the floating point stack should be saved when
350 * the isr runs. Must be false for x86 drivers
351 * RETURNS: Status
352 * IRQL: PASSIVE_LEVEL
353 */
354 {
355 PKINTERRUPT Interrupt;
356 NTSTATUS Status = STATUS_SUCCESS;
357
358 ASSERT_IRQL(PASSIVE_LEVEL);
359
360 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
361
362 /*
363 * Check the parameters
364 */
365 if (Vector >= NR_IRQS)
366 {
367 return(STATUS_INVALID_PARAMETER);
368 }
369 if (FloatingSave == TRUE)
370 {
371 return(STATUS_INVALID_PARAMETER);
372 }
373
374 /*
375 * Initialize interrupt object
376 */
377 Interrupt=ExAllocatePool(NonPagedPool,sizeof(KINTERRUPT));
378 if (Interrupt==NULL)
379 {
380 return(STATUS_INSUFFICIENT_RESOURCES);
381 }
382
383 Status = KeInitializeInterrupt(Interrupt,
384 ServiceRoutine,
385 ServiceContext,
386 SpinLock,
387 Vector,
388 Irql,
389 SynchronizeIrql,
390 InterruptMode,
391 ShareVector,
392 ProcessorEnableMask,
393 FloatingSave);
394 if (!NT_SUCCESS(Status))
395 {
396 ExFreePool(Interrupt);
397 return Status;
398 }
399
400 Status = KeConnectInterrupt(Interrupt);
401 if (!NT_SUCCESS(Status))
402 {
403 ExFreePool(Interrupt);
404 return Status;
405 }
406
407 *InterruptObject = Interrupt;
408
409 return(STATUS_SUCCESS);
410 }
411
412
413 VOID
414 STDCALL
415 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
416 /*
417 * FUNCTION: Releases a drivers isr
418 * ARGUMENTS:
419 * InterruptObject = isr to release
420 */
421 {
422 KeDisconnectInterrupt(InterruptObject);
423 ExFreePool(InterruptObject);
424 }
425
426 /* EOF */