Initial revision
[reactos.git] / reactos / ntoskrnl / hal / x86 / irq.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: mkernel/hal/x86/irq.c
5 * PURPOSE: IRQ handling
6 * PROGRAMMER: David Welch (welch@mcmail.com)
7 * UPDATE HISTORY:
8 * 29/05/98: Created
9 */
10
11 /*
12 * NOTE: In general the PIC interrupt priority facilities are used to
13 * preserve the NT IRQL semantics, global interrupt disables are only used
14 * to keep the PIC in a consistent state
15 *
16 */
17
18 /* INCLUDES ****************************************************************/
19
20 #include <ddk/ntddk.h>
21
22 #include <internal/stddef.h>
23 #include <internal/kernel.h>
24 #include <internal/bitops.h>
25 #include <internal/linkage.h>
26 #include <internal/string.h>
27
28 #include <internal/hal/segment.h>
29 #include <internal/hal/io.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* GLOBALS *****************************************************************/
35
36 #define NR_IRQS (16)
37 #define IRQ_BASE (0x20)
38
39 asmlinkage void irq_handler_0(void);
40 asmlinkage void irq_handler_1(void);
41 asmlinkage void irq_handler_2(void);
42 asmlinkage void irq_handler_3(void);
43 asmlinkage void irq_handler_4(void);
44 asmlinkage void irq_handler_5(void);
45 asmlinkage void irq_handler_6(void);
46 asmlinkage void irq_handler_7(void);
47 asmlinkage void irq_handler_8(void);
48 asmlinkage void irq_handler_9(void);
49 asmlinkage void irq_handler_10(void);
50 asmlinkage void irq_handler_11(void);
51 asmlinkage void irq_handler_12(void);
52 asmlinkage void irq_handler_13(void);
53 asmlinkage void irq_handler_14(void);
54 asmlinkage void irq_handler_15(void);
55
56 static unsigned int irq_handler[NR_IRQS]=
57 {
58 (int)&irq_handler_0,
59 (int)&irq_handler_1,
60 (int)&irq_handler_2,
61 (int)&irq_handler_3,
62 (int)&irq_handler_4,
63 (int)&irq_handler_5,
64 (int)&irq_handler_6,
65 (int)&irq_handler_7,
66 (int)&irq_handler_8,
67 (int)&irq_handler_9,
68 (int)&irq_handler_10,
69 (int)&irq_handler_11,
70 (int)&irq_handler_12,
71 (int)&irq_handler_13,
72 (int)&irq_handler_14,
73 (int)&irq_handler_15,
74 };
75
76 /*
77 * PURPOSE: Object describing each isr
78 * NOTE: The data in this table is only modified at passsive level but can
79 * be accessed at any irq level.
80 */
81 static LIST_ENTRY isr_table[NR_IRQS]={{NULL,NULL},};
82 static PKSPIN_LOCK isr_lock[NR_IRQS];
83 static KSPIN_LOCK isr_table_lock;
84
85 /* FUNCTIONS ****************************************************************/
86
87
88 #define PRESENT (0x8000)
89 #define I486_INTERRUPT_GATE (0xe00)
90
91 asmlinkage void KiInterruptDispatch(unsigned int irq)
92 /*
93 * FUNCTION: Calls the irq specific handler for an irq
94 * ARGUMENTS:
95 * irq = IRQ that has interrupted
96 */
97 {
98 KIRQL old_level;
99 PKINTERRUPT isr;
100 PLIST_ENTRY current;
101
102 /*
103 * Notify the rest of the kernel of the raised irq level
104 */
105 old_level = KeGetCurrentIrql();
106 DPRINT("old_level %d\n",old_level);
107 KeSetCurrentIrql(HIGH_LEVEL - irq);
108
109 /*
110 * Enable interrupts
111 * NOTE: Only higher priority interrupts will get through
112 */
113 __asm__("sti\n\t");
114
115 if (irq==0)
116 {
117 KeTimerInterrupt();
118 }
119 else
120 {
121 DPRINT("KiInterruptDispatch(irq %x)\n",irq);
122 /*
123 * Iterate the list until one of the isr tells us its device interrupted
124 */
125 current = isr_table[irq].Flink;
126 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
127 DPRINT("current %x isr %x\n",current,isr);
128 while (current!=NULL && !isr->ServiceRoutine(isr,isr->ServiceContext))
129 {
130 current = current->Flink;
131 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
132 DPRINT("current %x isr %x\n",current,isr);
133 }
134 }
135
136 /*
137 * Disable interrupts
138 */
139 __asm__("cli\n\t");
140
141 /*
142 * Send EOI to the PIC
143 */
144 outb(0x20,0x20);
145 if (irq>=8)
146 {
147 outb(0xa0,0x20);
148 }
149
150 /*
151 * Unmask the related irq
152 */
153 if (irq<8)
154 {
155 outb(0x21,inb(0x21)&(~(1<<irq)));
156 }
157 else
158 {
159 outb(0xa1,inb(0xa1)&(~(1<<(irq-8))));
160 }
161
162 /*
163 * If the processor level will drop below dispatch level on return then
164 * issue a DPC queue drain interrupt
165 */
166 if (old_level < DISPATCH_LEVEL)
167 {
168 KeSetCurrentIrql(DISPATCH_LEVEL);
169 __asm__("sti\n\t");
170 KiDispatchInterrupt(irq);
171 }
172 KeSetCurrentIrql(old_level);
173 }
174
175 void InitalizeIRQ(void)
176 {
177 int i;
178
179 /*
180 * First mask off all interrupts from pic
181 */
182 outb(0x21,0xff);
183 outb(0xa1,0xff);
184
185
186 /*
187 * Setup the IDT entries to point to the interrupt handlers
188 */
189 for (i=0;i<NR_IRQS;i++)
190 {
191 idt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
192 idt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
193 I486_INTERRUPT_GATE;
194 InitializeListHead(&isr_table[i]);
195 }
196 }
197
198 NTSTATUS IoConnectInterrupt(PKINTERRUPT* InterruptObject,
199 PKSERVICE_ROUTINE ServiceRoutine,
200 PVOID ServiceContext,
201 PKSPIN_LOCK SpinLock,
202 ULONG Vector,
203 KIRQL Irql,
204 KIRQL SynchronizeIrql,
205 KINTERRUPT_MODE InterruptMode,
206 BOOLEAN ShareVector,
207 KAFFINITY ProcessorEnableMask,
208 BOOLEAN FloatingSave)
209 /*
210 * FUNCTION: Registers a driver's isr to be called when its device interrupts
211 * ARGUMENTS:
212 * InterruptObject (OUT) = Points to the interrupt object created on
213 * return
214 * ServiceRoutine = Routine to be called when the device interrupts
215 * ServiceContext = Parameter to be passed to ServiceRoutine
216 * SpinLock = Initalized spinlock that will be used to synchronize
217 * access between the isr and other driver routines. This is
218 * required if the isr handles more than one vector or the
219 * driver has more than one isr
220 * Vector = Interrupt vector to allocate
221 * (returned from HalGetInterruptVector)
222 * Irql = DIRQL returned from HalGetInterruptVector
223 * SynchronizeIrql = DIRQL at which the isr will execute. This must
224 * be the highest of all the DIRQLs returned from
225 * HalGetInterruptVector if the driver has multiple
226 * isrs
227 * InterruptMode = Specifies if the interrupt is LevelSensitive or
228 * Latched
229 * ShareVector = Specifies if the vector can be shared
230 * ProcessorEnableMask = Processors on the isr can run
231 * FloatingSave = TRUE if the floating point stack should be saved when
232 * the isr runs. Must be false for x86 drivers
233 * RETURNS: Status
234 * IRQL: PASSIVE_LEVEL
235 */
236 {
237 KIRQL oldlvl;
238 KIRQL synch_oldlvl;
239 PKINTERRUPT ListHead;
240
241 ASSERT_IRQL(PASSIVE_LEVEL);
242
243 /*
244 * Check the parameters
245 */
246 if (Vector >= NR_IRQS)
247 {
248 return(STATUS_INVALID_PARAMETER);
249 }
250 if (FloatingSave == TRUE)
251 {
252 return(STATUS_INVALID_PARAMETER);
253 }
254
255 /*
256 * Acquire the table spinlock
257 */
258 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
259
260 /*
261 * Check if the vector is already in use that we can share it
262 */
263 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
264 if (!IsListEmpty(&isr_table[Vector]) &&
265 (ShareVector == FALSE || ListHead->Shareable==FALSE))
266 {
267 KeReleaseSpinLock(&isr_table_lock,oldlvl);
268 return(STATUS_INVALID_PARAMETER);
269 }
270 else
271 {
272 isr_lock[Vector]=ExAllocatePool(NonPagedPool,sizeof(KSPIN_LOCK));
273 KeInitializeSpinLock(isr_lock[Vector]);
274 isr_lock[Vector]->irql = SynchronizeIrql;
275 }
276
277 /*
278 * Initialize interrupt object
279 */
280 (*InterruptObject)=ExAllocatePool(NonPagedPool,sizeof(KINTERRUPT));
281 if ((*InterruptObject)==NULL)
282 {
283 return(STATUS_INSUFFICIENT_RESOURCES);
284 }
285 (*InterruptObject)->ServiceContext = ServiceContext;
286 (*InterruptObject)->ServiceRoutine = ServiceRoutine;
287 (*InterruptObject)->Vector = Vector;
288 (*InterruptObject)->ProcessorEnableMask = ProcessorEnableMask;
289 (*InterruptObject)->SynchLevel = SynchronizeIrql;
290 (*InterruptObject)->Shareable = ShareVector;
291 (*InterruptObject)->FloatingSave = FALSE;
292 (*InterruptObject)->IrqLock = isr_lock[Vector];
293
294 KeRaiseIrql((*InterruptObject)->SynchLevel,&synch_oldlvl);
295 KeAcquireSpinLockAtDpcLevel((*InterruptObject)->IrqLock);
296 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
297 InsertTailList(&isr_table[Vector],&((*InterruptObject)->Entry));
298 DPRINT("%x %x\n",(*InterruptObject)->Entry.Flink,
299 (*InterruptObject)->Entry.Blink);
300 KeReleaseSpinLockFromDpcLevel((*InterruptObject)->IrqLock);
301 KeLowerIrql(synch_oldlvl);
302
303 /*
304 * Release the table spinlock
305 */
306 KeReleaseSpinLock(&isr_table_lock,oldlvl);
307
308 return(STATUS_SUCCESS);
309 }
310
311
312 VOID IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
313 /*
314 * FUNCTION: Releases a drivers isr
315 * ARGUMENTS:
316 * InterruptObject = isr to release
317 */
318 {
319 KIRQL oldlvl;
320
321 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
322 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
323 RemoveEntryFromList(&isr_table[InterruptObject->Vector],
324 &InterruptObject->Entry);
325 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
326 KeLowerIrql(oldlvl);
327 }
328
329 ULONG HalGetInterruptVector(INTERFACE_TYPE InterfaceType,
330 ULONG BusNumber,
331 ULONG BusInterruptLevel,
332 ULONG BusInterruptVector,
333 PKIRQL Irql,
334 PKAFFINITY Affinity)
335 /*
336 * FUNCTION: Returns a mapped system interrupt vector for passing to
337 * IoConnectInterrupt
338 * ARGUMENTS:
339 * InterfaceType = Type bus the device is on
340 * BusNumber = Zero based number of the bus
341 * BusInterruptLevel = Bus specific interrupt level
342 * BusInterruptVector = Bus specific interrupt vector
343 * Irql (IN/OUT) = On entry the bus relative IRQL
344 * On exit the DIRQL
345 * Affinity (OUT) = Caller supplied storage for the interrupt
346 * affinity mask
347 * RETURNS: The mapped vector
348 */
349 {
350 ULONG ret;
351
352 // ASSERT_IRQL(PASSIVE_LEVEL);
353
354 switch (InterfaceType)
355 {
356 case Internal:
357 *Irql = HIGH_LEVEL - BusInterruptVector;
358 ret = BusInterruptVector;
359 break;
360
361 default:
362 ret = -1;
363 printk("(%s:%d) Don't know that bus type\n",__FILE__,__LINE__);
364 break;
365 }
366 return(ret);
367 }
368
369