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