Switch to a simple X11-style license.
[polintos/scott/priv.git] / kernel / core / irq.cc
1 // core/irq.cc -- generic IRQ handling
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
21 // SOFTWARE.
22
23
24 #include <kern/kernel.h>
25 #include <kern/irq.h>
26
27 namespace IRQ {
28         void InterruptController::request_int(InterruptSlot *slot, Interrupt *irq)
29         {
30                 AutoSpinLockIRQ autolock(lock);
31                 
32                 u32 irqnum = get_irqnum(slot);
33                 Interrupt *list = slot->first_int;
34                 Interrupt **insert = &slot->first_int;
35                 
36                 while (list) {
37                         if (list == irq)
38                                 throw_idl(ResourceBusy, -1, countarray("IRQ in use"));
39                         
40                         insert = &list->next;
41                         list = *insert;
42                 }
43                 
44                 *insert = irq;
45                 irq->slot = slot;
46                 irq->controller = this;
47                 
48                 if (insert == &slot->first_int && slot->mask_count == 0) {
49                         // There were no existing handlers, so it's currently masked
50                         unmask(irqnum);
51                 }
52         }
53         
54         void InterruptController::free_int(Interrupt *irq)
55         {
56                 InterruptSlot *slot = irq->slot;
57                 u32 irqnum = get_irqnum(slot);
58         
59                 DroppableAutoSpinLockIRQ autolock(lock);
60                 
61                 Interrupt *list = slot->first_int;
62                 Interrupt **remove = &slot->first_int;
63
64                 while (list) {
65                         if (list == irq) {
66                                 *remove = list->next;
67
68                                 if (remove == &slot->first_int && !list->next &&
69                                     slot->mask_count == 0)
70                                 {
71                                         // It was the last handler for this IRQ num.
72                                         mask(irqnum);
73                                 }
74
75                                 autolock.unlock();
76                                 wait_for_irq(slot);
77                                 return;
78                         }
79                         
80                         remove = &list->next;
81                         list = *remove;
82                 }
83                 
84                 throw_idl(InvalidReference, 0, nullarray);
85         }
86         
87         bool InterruptController::handle_irq(int irq)
88         {
89                 DroppableAutoSpinLock autolock(lock);
90                 bool handled_one = false;
91                 bool irq_specified = irq >= 0;
92         
93                 in_irq = true;
94
95                 do {
96                         if (!irq_specified) {
97                                 irq = get_pending_irq();
98                                 
99                                 if (irq < 0)
100                                         break;
101                         }
102                         
103                         mask_and_ack(irq);
104
105                         InterruptSlot *slot = get_slot(irq);
106                         
107                         if (slot->mask_count > 0 || slot->flags & InterruptSlot::Running) {
108                                 slot->flags |= InterruptSlot::Pending;
109                                 continue;
110                         }
111                                 
112                         slot->flags |= InterruptSlot::Running;
113                         assert(!(slot->flags & InterruptSlot::Pending));
114                 
115                         autolock.unlock();
116
117                         for (Interrupt *i = slot->first_int; i; i = i->next)
118                                 handled_one |= i->action();
119
120                         autolock.lock();
121
122                         slot->flags &= ~InterruptSlot::Running;
123                         
124                         if (slot->mask_count == 0)
125                                 unmask(irq);
126                 } while (!irq_specified);
127
128                 in_irq = false;
129                 return handled_one;
130         }
131         
132         void InterruptController::rec_mask_nowait(InterruptSlot *slot)
133         {
134                 AutoSpinLockIRQ autolock(lock);
135                 u32 irq = get_irqnum(slot);
136                 
137                 if (slot->mask_count++ == 0)
138                         mask(irq);
139         }
140         
141         void InterruptController::rec_mask(InterruptSlot *slot)
142         {
143                 rec_mask_nowait(slot);
144                 wait_for_irq(slot);
145         }
146
147         void InterruptController::rec_unmask(InterruptSlot *slot)
148         {
149                 bool was_pending = false;
150                 DroppableAutoSpinLockRecIRQ autolock(lock);
151                 
152                 if (--slot->mask_count == 0) {
153                         unmask(get_irqnum(slot));
154                         
155                         if (slot->flags & InterruptSlot::Pending) {
156                                 was_pending = true;
157                                 slot->flags &= ~InterruptSlot::Pending;
158                                 slot->flags |= InterruptSlot::Running;
159                         }
160                 }
161
162                 autolock.unlock();
163
164                 if (was_pending) {
165                         // Only really necessary for edge-triggered interrupts
166                         
167                         for (Interrupt *i = slot->first_int; i; i = i->next)
168                                 i->action();
169                         
170                         autolock.lock();
171                         slot->flags &= ~InterruptSlot::Running;
172                         autolock.unlock();
173                 }
174         }
175         
176         void InterruptController::wait_for_irq(InterruptSlot *slot)
177         {
178                 while (slot->flags & InterruptSlot::Running)
179                         ll_busywait();
180         }
181         
182         bool in_irq;
183 }
184
185 //#include "irq-server/footer.cc"