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