]> git.buserror.net Git - polintos/scott/priv.git/blob - kernel/core/irq.cc
Use GCC builtins for bit scanning. The minor benefit is that it is
[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 // This software is provided 'as-is', without any express or implied warranty.
6 // In no event will the authors or contributors be held liable for any damages
7 // arising from the use of this software.
8 // 
9 // Permission is hereby granted to everyone, free of charge, to use, copy,
10 // modify, prepare derivative works of, publish, distribute, perform,
11 // sublicense, and/or sell copies of the Software, provided that the above
12 // copyright notice and disclaimer of warranty be included in all copies or
13 // substantial portions of this software.
14
15
16 #include <kern/kernel.h>
17 #include <kern/irq.h>
18
19 namespace IRQ {
20         void InterruptController::request_int(InterruptSlot *slot, Interrupt *irq)
21         {
22                 AutoSpinLockIRQ autolock(lock);
23                 
24                 u32 irqnum = get_irqnum(slot);
25                 Interrupt *list = slot->first_int;
26                 Interrupt **insert = &slot->first_int;
27                 
28                 while (list) {
29                         if (list == irq)
30                                 throw_idl(ResourceBusy, -1, countarray("IRQ in use"));
31                         
32                         insert = &list->next;
33                         list = *insert;
34                 }
35                 
36                 *insert = irq;
37                 irq->slot = slot;
38                 irq->controller = this;
39                 
40                 if (insert == &slot->first_int && slot->mask_count == 0) {
41                         // There were no existing handlers, so it's currently masked
42                         unmask(irqnum);
43                 }
44         }
45         
46         void InterruptController::free_int(Interrupt *irq)
47         {
48                 InterruptSlot *slot = irq->slot;
49                 u32 irqnum = get_irqnum(slot);
50         
51                 DroppableAutoSpinLockIRQ autolock(lock);
52                 
53                 Interrupt *list = slot->first_int;
54                 Interrupt **remove = &slot->first_int;
55
56                 while (list) {
57                         if (list == irq) {
58                                 *remove = list->next;
59
60                                 if (remove == &slot->first_int && !list->next &&
61                                     slot->mask_count == 0)
62                                 {
63                                         // It was the last handler for this IRQ num.
64                                         mask(irqnum);
65                                 }
66
67                                 autolock.unlock();
68                                 wait_for_irq(slot);
69                                 return;
70                         }
71                         
72                         remove = &list->next;
73                         list = *remove;
74                 }
75                 
76                 throw_idl(InvalidReference, 0, nullarray);
77         }
78         
79         bool InterruptController::handle_irq(int irq)
80         {
81                 DroppableAutoSpinLock autolock(lock);
82                 bool handled_one = false;
83                 bool irq_specified = irq >= 0;
84         
85                 in_irq = true;
86
87                 do {
88                         if (!irq_specified) {
89                                 irq = get_pending_irq();
90                                 
91                                 if (irq < 0)
92                                         break;
93                         }
94                         
95                         mask_and_ack(irq);
96
97                         InterruptSlot *slot = get_slot(irq);
98                         
99                         if (slot->mask_count > 0 || slot->flags & InterruptSlot::Running) {
100                                 slot->flags |= InterruptSlot::Pending;
101                                 continue;
102                         }
103                                 
104                         slot->flags |= InterruptSlot::Running;
105                         assert(!(slot->flags & InterruptSlot::Pending));
106                 
107                         autolock.unlock();
108
109                         for (Interrupt *i = slot->first_int; i; i = i->next)
110                                 handled_one |= i->action();
111
112                         autolock.lock();
113
114                         slot->flags &= ~InterruptSlot::Running;
115                         
116                         if (slot->mask_count == 0)
117                                 unmask(irq);
118                 } while (!irq_specified);
119
120                 in_irq = false;
121                 return handled_one;
122         }
123         
124         void InterruptController::rec_mask_nowait(InterruptSlot *slot)
125         {
126                 AutoSpinLockIRQ autolock(lock);
127                 u32 irq = get_irqnum(slot);
128                 
129                 if (slot->mask_count++ == 0)
130                         mask(irq);
131         }
132         
133         void InterruptController::rec_mask(InterruptSlot *slot)
134         {
135                 rec_mask_nowait(slot);
136                 wait_for_irq(slot);
137         }
138
139         void InterruptController::rec_unmask(InterruptSlot *slot)
140         {
141                 bool was_pending = false;
142                 DroppableAutoSpinLockRecIRQ autolock(lock);
143                 
144                 if (--slot->mask_count == 0) {
145                         unmask(get_irqnum(slot));
146                         
147                         if (slot->flags & InterruptSlot::Pending) {
148                                 was_pending = true;
149                                 slot->flags &= ~InterruptSlot::Pending;
150                                 slot->flags |= InterruptSlot::Running;
151                         }
152                 }
153
154                 autolock.unlock();
155
156                 if (was_pending) {
157                         // Only really necessary for edge-triggered interrupts
158                         
159                         for (Interrupt *i = slot->first_int; i; i = i->next)
160                                 i->action();
161                         
162                         autolock.lock();
163                         slot->flags &= ~InterruptSlot::Running;
164                         autolock.unlock();
165                 }
166         }
167         
168         void InterruptController::wait_for_irq(InterruptSlot *slot)
169         {
170                 while (slot->flags & InterruptSlot::Running)
171                         ll_busywait();
172         }
173         
174         bool in_irq;
175 }
176
177 //#include "irq-server/footer.cc"