forked from Stichting-MINIX-Research-Foundation/minix
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterrupt.c
178 lines (151 loc) · 4.8 KB
/
interrupt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
* The Minix hardware interrupt system.
*
* This file contains routines for managing the interrupt
* controller.
*
* put_irq_handler: register an interrupt handler.
* rm_irq_handler: deregister an interrupt handler.
* irq_handle: handle a hardware interrupt.
* called by the system dependent part when an
* external interrupt occures.
* enable_irq: enable hook for IRQ.
* disable_irq: disable hook for IRQ.
*/
#include <assert.h>
#include "kernel/kernel.h"
#include "archconst.h"
#include "hw_intr.h"
/* number of lists of IRQ hooks, one list per supported line. */
static irq_hook_t* irq_handlers[NR_IRQ_VECTORS] = {0};
/*===========================================================================*
* put_irq_handler *
*===========================================================================*/
/* Register an interrupt handler. */
void put_irq_handler( irq_hook_t* hook, int irq,
const irq_handler_t handler)
{
int id;
irq_hook_t **line;
unsigned long bitmap;
if( irq < 0 || irq >= NR_IRQ_VECTORS )
panic("invalid call to put_irq_handler: %d", irq);
line = &irq_handlers[irq];
bitmap = 0;
while ( *line != NULL ) {
if(hook == *line) return; /* extra initialization */
bitmap |= (*line)->id; /* mark ids in use */
line = &(*line)->next;
}
/* find the lowest id not in use */
for (id = 1; id != 0; id <<= 1)
if (!(bitmap & id)) break;
if(id == 0)
panic("Too many handlers for irq: %d", irq);
hook->next = NULL;
hook->handler = handler;
hook->irq = irq;
hook->id = id;
*line = hook;
/* And as last enable the irq at the hardware.
*
* Internal this activates the line or source of the given interrupt.
*/
if((irq_actids[hook->irq] &= ~hook->id) == 0) {
hw_intr_used(irq);
hw_intr_unmask(hook->irq);
}
}
/*===========================================================================*
* rm_irq_handler *
*===========================================================================*/
/* Unregister an interrupt handler. */
void rm_irq_handler( const irq_hook_t* hook ) {
const int irq = hook->irq;
const int id = hook->id;
irq_hook_t **line;
if( irq < 0 || irq >= NR_IRQ_VECTORS )
panic("invalid call to rm_irq_handler: %d", irq);
/* remove the hook */
line = &irq_handlers[irq];
while( (*line) != NULL ) {
if((*line)->id == id) {
(*line) = (*line)->next;
if (irq_actids[irq] & id)
irq_actids[irq] &= ~id;
}
else {
line = &(*line)->next;
}
}
/* Disable the irq if there are no other handlers registered.
* If the irq is shared, reenable it if there is no active handler.
*/
if (irq_handlers[irq] == NULL) {
hw_intr_mask(irq);
hw_intr_not_used(irq);
}
else if (irq_actids[irq] == 0) {
hw_intr_unmask(irq);
}
}
/*===========================================================================*
* irq_handle *
*===========================================================================*/
/*
* The function first disables interrupt is need be and restores the state at
* the end. Before returning, it unmasks the IRQ if and only if all active ID
* bits are cleared, and restart a process.
*/
void irq_handle(int irq)
{
irq_hook_t * hook;
/* here we need not to get this IRQ until all the handlers had a say */
assert(irq >= 0 && irq < NR_IRQ_VECTORS);
hw_intr_mask(irq);
hook = irq_handlers[irq];
/* Check for spurious interrupts. */
if(hook == NULL) {
static int nspurious[NR_IRQ_VECTORS], report_interval = 100;
nspurious[irq]++;
if(nspurious[irq] == 1 || !(nspurious[irq] % report_interval)) {
printf("irq_handle: spurious irq %d (count: %d); keeping masked\n",
irq, nspurious[irq]);
if(report_interval < INT_MAX/2)
report_interval *= 2;
}
return;
}
/* Call list of handlers for an IRQ. */
while( hook != NULL ) {
/* For each handler in the list, mark it active by setting its ID bit,
* call the function, and unmark it if the function returns true.
*/
irq_actids[irq] |= hook->id;
/* Call the hooked function. */
if( (*hook->handler)(hook) )
irq_actids[hook->irq] &= ~hook->id;
/* Next hooked function. */
hook = hook->next;
}
/* reenable the IRQ only if there is no active handler */
if (irq_actids[irq] == 0)
hw_intr_unmask(irq);
hw_intr_ack(irq);
}
/* Enable/Disable a interrupt line. */
void enable_irq(const irq_hook_t *hook)
{
if((irq_actids[hook->irq] &= ~hook->id) == 0) {
hw_intr_unmask(hook->irq);
}
}
/* Return true if the interrupt was enabled before call. */
int disable_irq(const irq_hook_t *hook)
{
if(irq_actids[hook->irq] & hook->id) /* already disabled */
return 0;
irq_actids[hook->irq] |= hook->id;
hw_intr_mask(hook->irq);
return TRUE;
}