c1496e0b6d073ba70950b3d4bfe6e186ba6632f4
[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 conditions:
11 // 
12 //     * Redistributions of source code must retain the above copyright notice,
13 //       this list of conditions and the following disclaimers.
14 // 
15 //     * Redistributions in binary form must reproduce the above copyright notice,
16 //       this list of conditions and the following disclaimers in the
17 //       documentation and/or other materials provided with the distribution.
18 // 
19 //     * The names of the Software's authors and/or contributors
20 //       may not be used to endorse or promote products derived from
21 //       this Software without specific prior written permission.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
25 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
29 // SOFTWARE.
30
31
32 #include <kern/kernel.h>
33 #include <kern/irq.h>
34
35 namespace IRQ {
36         void InterruptController::request_int(InterruptSlot *slot, Interrupt *irq)
37         {
38                 AutoSpinLockIRQ autolock(lock);
39                 
40                 u32 irqnum = get_irqnum(slot);
41                 Interrupt *list = slot->first_int;
42                 Interrupt **insert = &slot->first_int;
43                 
44                 while (list) {
45                         if (list == irq)
46                                 throw_idl(ResourceBusy, -1, countarray("IRQ in use"));
47                         
48                         insert = &list->next;
49                         list = *insert;
50                 }
51                 
52                 *insert = irq;
53                 irq->slot = slot;
54                 irq->controller = this;
55                 
56                 if (insert == &slot->first_int && slot->mask_count == 0) {
57                         // There were no existing handlers, so it's currently masked
58                         unmask(irqnum);
59                 }
60         }
61         
62         void InterruptController::free_int(Interrupt *irq)
63         {
64                 InterruptSlot *slot = irq->slot;
65                 u32 irqnum = get_irqnum(slot);
66         
67                 DroppableAutoSpinLockIRQ autolock(lock);
68                 
69                 Interrupt *list = slot->first_int;
70                 Interrupt **remove = &slot->first_int;
71
72                 while (list) {
73                         if (list == irq) {
74                                 *remove = list->next;
75
76                                 if (remove == &slot->first_int && !list->next &&
77                                     slot->mask_count == 0)
78                                 {
79                                         // It was the last handler for this IRQ num.
80                                         mask(irqnum);
81                                 }
82
83                                 autolock.unlock();
84                                 wait_for_irq(slot);
85                                 return;
86                         }
87                         
88                         remove = &list->next;
89                         list = *remove;
90                 }
91                 
92                 throw_idl(InvalidReference, 0, nullarray);
93         }
94         
95         bool InterruptController::handle_irq(int irq)
96         {
97                 DroppableAutoSpinLock autolock(lock);
98                 bool handled_one = false;
99                 bool irq_specified = irq >= 0;
100         
101                 in_irq = true;
102
103                 do {
104                         if (!irq_specified) {
105                                 irq = get_pending_irq();
106                                 
107                                 if (irq < 0)
108                                         break;
109                         }
110                         
111                         mask_and_ack(irq);
112
113                         InterruptSlot *slot = get_slot(irq);
114                         
115                         if (slot->mask_count > 0 || slot->flags & InterruptSlot::Running) {
116                                 slot->flags |= InterruptSlot::Pending;
117                                 continue;
118                         }
119                                 
120                         slot->flags |= InterruptSlot::Running;
121                         assert(!(slot->flags & InterruptSlot::Pending));
122                 
123                         autolock.unlock();
124
125                         for (Interrupt *i = slot->first_int; i; i = i->next)
126                                 handled_one |= i->action();
127
128                         autolock.lock();
129
130                         slot->flags &= ~InterruptSlot::Running;
131                         
132                         if (slot->mask_count == 0)
133                                 unmask(irq);
134                 } while (!irq_specified);
135
136                 in_irq = false;
137                 return handled_one;
138         }
139         
140         void InterruptController::rec_mask_nowait(InterruptSlot *slot)
141         {
142                 AutoSpinLockIRQ autolock(lock);
143                 u32 irq = get_irqnum(slot);
144                 
145                 if (slot->mask_count++ == 0)
146                         mask(irq);
147         }
148         
149         void InterruptController::rec_mask(InterruptSlot *slot)
150         {
151                 rec_mask_nowait(slot);
152                 wait_for_irq(slot);
153         }
154
155         void InterruptController::rec_unmask(InterruptSlot *slot)
156         {
157                 bool was_pending = false;
158                 DroppableAutoSpinLockRecIRQ autolock(lock);
159                 
160                 if (--slot->mask_count == 0) {
161                         unmask(get_irqnum(slot));
162                         
163                         if (slot->flags & InterruptSlot::Pending) {
164                                 was_pending = true;
165                                 slot->flags &= ~InterruptSlot::Pending;
166                                 slot->flags |= InterruptSlot::Running;
167                         }
168                 }
169
170                 autolock.unlock();
171
172                 if (was_pending) {
173                         // Only really necessary for edge-triggered interrupts
174                         
175                         for (Interrupt *i = slot->first_int; i; i = i->next)
176                                 i->action();
177                         
178                         autolock.lock();
179                         slot->flags &= ~InterruptSlot::Running;
180                         autolock.unlock();
181                 }
182         }
183         
184         void InterruptController::wait_for_irq(InterruptSlot *slot)
185         {
186                 while (slot->flags & InterruptSlot::Running)
187                         ll_busywait();
188         }
189         
190         bool in_irq;
191 }
192
193 //#include "irq-server/footer.cc"