]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/io/server/src/vicu.cc
update
[l4.git] / l4 / pkg / io / server / src / vicu.cc
1 /*
2  * (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
3  *     economic rights: Technische Universität Dresden (Germany)
4  *
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU General Public License 2.
7  * Please see the COPYING-GPL-2 file for details.
8  */
9
10 #include <l4/vbus/vdevice-ops.h>
11
12 #include "vicu.h"
13 #include "vbus_factory.h"
14 #include "server.h"
15 #include "main.h"
16 #include "debug.h"
17
18 #include <l4/re/util/cap_alloc>
19 #include <l4/re/namespace>
20 #include <l4/re/env>
21 #include <l4/re/error_helper>
22 #include <l4/sys/icu>
23 #include <l4/sys/irq>
24 #include <l4/sys/debugger.h>
25 #include <l4/sys/kdebug.h>
26
27 #include <cassert>
28
29 namespace Vi {
30
31 using L4Re::Util::Auto_cap;
32 using L4Re::chksys;
33 using L4Re::chkcap;
34
35 enum { MAX_HW_IRQS = 256 };
36
37 Kernel_irq_pin *Sw_icu::_real_irqs[MAX_HW_IRQS];
38
39 Kernel_irq_pin *
40 Sw_icu::real_irq(unsigned n)
41 {
42   if (n >= sizeof(_real_irqs)/sizeof(_real_irqs[0]))
43     return 0;
44
45   if (!_real_irqs[n])
46     _real_irqs[n] = new Kernel_irq_pin(n);
47
48   return _real_irqs[n];
49 }
50
51
52 Sw_icu::Sw_icu()
53 {
54   add_feature(this);
55   registry->register_obj(this);
56 }
57
58 Sw_icu::~Sw_icu()
59 {
60   registry->unregister_obj(this);
61 }
62
63 int
64 Sw_icu::bind_irq(l4_msgtag_t tag, unsigned irqn, L4::Snd_fpage const &/*irqc*/)
65 {
66   if (tag.items() < 1)
67     return -L4_EINVAL;
68
69   d_printf(DBG_ALL, "%s[%p]: bind_irq(%d, ...)\n", name(), this, irqn);
70
71   Irq_set::Iterator i = _irqs.find(irqn);
72   if (i == _irqs.end())
73     return -L4_ENOENT;
74
75   int err = i->bind(rcv_cap);
76   if (err < 0)
77     d_printf(DBG_ERR, "ERROR: binding irq %d, result is %d (%s)\n", irqn, err, l4sys_errtostr(err));
78
79   return err;
80 }
81
82 int
83 Sw_icu::unbind_irq(l4_msgtag_t tag, unsigned irqn, L4::Snd_fpage const &/*irqc*/)
84 {
85   if (tag.items() < 1)
86     return -L4_EINVAL;
87
88   d_printf(DBG_ALL, "%s[%p]: unbind_irq(%d, ...)\n", name(), this, irqn);
89
90   Irq_set::Iterator i = _irqs.find(irqn);
91   if (i == _irqs.end())
92     return -L4_ENOENT;
93
94   // could check the validity of the cap too, however we just don't care
95   return i->unbind();
96 }
97
98 int
99 Sw_icu::set_mode(l4_msgtag_t /*tag*/, unsigned irqn, l4_umword_t mode)
100 {
101   Irq_set::Iterator i = _irqs.find(irqn);
102   if (i == _irqs.end())
103     return -L4_ENOENT;
104
105   if (i->l4_type() != (mode & 0x6))
106     {
107       d_printf(DBG_WARN, "WARNING: Changing type of IRQ %d from %x to %lx prohibited\n",
108              irqn, i->l4_type(), mode);
109       return 0;
110     }
111   return 0;
112 }
113
114 int
115 Sw_icu::unmask_irq(l4_msgtag_t /*tag*/, unsigned irqn)
116 {
117   Irq_set::Iterator i = _irqs.find(irqn);
118   if (i == _irqs.end())
119     return -L4_ENOENT;
120
121   if (!i->unmask_via_icu())
122     return -L4_EINVAL;
123
124   return i->unmask();
125 }
126
127
128 bool
129 Sw_icu::irqs_allocated(Adr_resource const *r)
130 {
131   for (unsigned n = r->_data().start(); n <= r->_data().end(); ++n)
132     {
133       if (_irqs.find(n) == _irqs.end())
134         return false;
135     }
136
137   return true;
138 }
139
140 bool
141 Sw_icu::add_irqs(Adr_resource const *r)
142 {
143   for (unsigned n = r->_data().start(); n <= r->_data().end(); ++n)
144     {
145       if (_irqs.find(n) != _irqs.end())
146         continue;
147
148       Sw_irq_pin *irq = new Sw_irq_pin(real_irq(n), n, r->flags());
149       _irqs.insert(irq);
150     }
151   return true;
152 }
153
154 bool
155 Sw_icu::add_irq(unsigned n, unsigned flags, Io_irq_pin *be)
156 {
157   if (_irqs.find(n) == _irqs.end())
158     return false;
159
160   Sw_irq_pin *irq = new Sw_irq_pin(be, n, flags);
161   _irqs.insert(irq);
162   return true;
163 }
164
165 int
166 Sw_icu::alloc_irq(unsigned flags, Io_irq_pin *be)
167 {
168   unsigned i;
169   for (i = 1; i < 1000; ++i)
170     if (_irqs.find(i) == _irqs.end())
171       break;
172
173   if (i == 1000)
174     return -1;
175
176   Sw_irq_pin *irq = new Sw_irq_pin(be, i, flags);
177   _irqs.insert(irq);
178   return i;
179 }
180
181
182 int
183 Sw_icu::dispatch(l4_umword_t /*obj*/, L4::Ipc_iostream &ios)
184 {
185   l4_umword_t op, irqn;
186   L4::Snd_fpage irqc;
187   l4_msgtag_t tag;
188   ios >> tag >> op >> irqn;
189
190   if (tag.label() != L4_PROTO_IRQ)
191     return -L4_EBADPROTO;
192
193   switch (op)
194     {
195     case L4_ICU_OP_BIND:
196       ios >> irqc;
197       return bind_irq(tag, irqn, irqc);
198
199     case L4_ICU_OP_UNBIND:
200       ios >> irqc;
201       return unbind_irq(tag, irqn, irqc);
202
203     case L4_ICU_OP_UNMASK:
204       unmask_irq(tag, irqn);
205       return -L4_ENOREPLY;
206
207     case L4_ICU_OP_SET_MODE:
208         {
209           l4_umword_t mode;
210           ios >> mode;
211           return set_mode(tag, irqn, mode);
212         }
213
214     default: return -L4_ENOSYS;
215     }
216 }
217
218 int
219 Sw_icu::dispatch(l4_umword_t, l4_uint32_t func, L4::Ipc_iostream &ios)
220 {
221   if (func != L4vbus_vicu_get_cap)
222     return -L4_ENOSYS;
223
224   ios << obj_cap();
225   return L4_EOK;
226 }
227
228 //static VBus_factory<Sw_icu> __vicu_factory("Vicu");
229
230 int
231 Sw_icu::Sw_irq_pin::trigger() const
232 {
233   return l4_error(_irq->trigger());
234 }
235
236
237 unsigned
238 Sw_icu::Sw_irq_pin::l4_type() const
239 {
240   unsigned m = type();
241   unsigned r = (m & S_irq_type_mask) / Adr_resource::Irq_info_factor;
242   return r;
243 }
244
245 void
246 Sw_icu::Sw_irq_pin::allocate_master_irq()
247 {
248   assert (_master->shared());
249   Auto_cap<L4::Irq>::Cap lirq = chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(),
250       "allocating IRQ capability");
251   // printf("IRQ mode = %x -> %x\n", type(), l4_type());
252   chksys(L4Re::Env::env()->factory()->create(lirq.get(), L4_PROTO_IRQ) << l4_umword_t(1), "allocating IRQ");
253   chksys(_master->bind(lirq.get(), l4_type()), "binding IRQ");
254   _master->irq(lirq.release());
255   _master->set_chained(true);
256 }
257
258
259 int
260 Sw_icu::Sw_irq_pin::bind(L4::Cap<void> rc)
261 {
262   if (_irq.is_valid())
263     {
264       if (_irq.get().validate(L4Re::This_task).label() > 0)
265         return -L4_EEXIST;
266
267       _unbind();
268     }
269
270   if (bound())
271     return -L4_EPERM;
272
273   Auto_cap<L4::Irq>::Cap irq =
274     chkcap(L4Re::Util::cap_alloc.alloc<L4::Irq>(), "allocating IRQ capability");
275
276   irq.get().move(L4::cap_cast<L4::Irq>(rc));
277
278   if (_master->shared() && !_master->chained() && _master->sw_irqs() == 0)
279     {
280       allocate_master_irq();
281       assert (_master->chained());
282     }
283
284   if (!_master->chained())
285     {
286       // the first irq shall be attached to a hw irq
287       d_printf(DBG_DEBUG2, "IRQ %d -> client\nIRQ mode = %x -> %x\n",
288               irqn(), type(), l4_type());
289       int err = _master->bind(irq.get(), l4_type());
290       if (err < 0)
291         return err;
292
293       _irq = irq;
294       _master->irq(_irq.get());
295       _master->inc_sw_irqs();
296       _state |= S_bound;
297       if (err == 1)
298         _state |= S_unmask_via_icu;
299
300       d_printf(DBG_DEBUG2, "  bound irq %u -> err=%d\n", irqn(), err);
301       return err;
302     }
303
304   d_printf(DBG_DEBUG2, "IRQ %d -> proxy -> %d clients\n", irqn(), _master->sw_irqs() + 1);
305   L4Re::chksys(_master->irq()->chain(l4_umword_t(_master), irq.get()), "attach");
306   _irq = irq;
307   _master->inc_sw_irqs();
308
309   return 0;
310 }
311
312 int
313 Sw_icu::Sw_irq_pin::_unbind()
314 {
315   int err = 0;
316   _master->dec_sw_irqs();
317   if (_master->sw_irqs() == 0)
318     {
319       if (_master->chained())
320         L4Re::Util::cap_alloc.free(_master->irq());
321
322       _master->irq(L4::Cap<L4::Irq>::Invalid);
323       _master->set_chained(false);
324     }
325
326   _irq = L4::Cap<L4::Irq>::Invalid;
327
328   _state &= ~S_bound;
329   return err;
330 }
331
332 int
333 Sw_icu::Sw_irq_pin::unbind()
334 {
335   if (!_master)
336     return -L4_EINVAL;
337
338   if (!_master->sw_irqs())
339     return -L4_EINVAL;
340
341   return _unbind();
342 }
343
344 }