source: mainline/kernel/generic/src/ddi/irq.c@ 36df4109

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 36df4109 was b366a6f4, checked in by Martin Decky <martin@…>, 14 years ago

automatic kernel console lockout

  • kernel automatically relinquishes the access to the kernel console when the uspace maps the respective physical memory area
  • kernel output before uspace initialization is currently broken on Ski (no physical memory area), but this is pending further unification
  • kernel console devices are now independent (there is no system-wide "silent" variable), thus on multiple devices the kernel console and uspace output might be usable at the same time
  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericddi
30 * @{
31 */
32/**
33 * @file
34 * @brief IRQ dispatcher.
35 *
36 * This file provides means of connecting IRQs with particular
37 * devices and logic for dispatching interrupts to IRQ handlers
38 * defined by those devices.
39 *
40 * This code is designed to support:
41 * - multiple devices sharing single IRQ
42 * - multiple IRQs per single device
43 * - multiple instances of the same device
44 *
45 *
46 * Note about architectures.
47 *
48 * Some architectures has the term IRQ well defined. Examples
49 * of such architectures include amd64, ia32 and mips32. Some
50 * other architectures, such as sparc64, don't use the term
51 * at all. In those cases, we boldly step forward and define what
52 * an IRQ is.
53 *
54 * The implementation is generic enough and still allows the
55 * architectures to use the hardware layout effectively.
56 * For instance, on amd64 and ia32, where there is only 16
57 * IRQs, the irq_hash_table can be optimized to a one-dimensional
58 * array. Next, when it is known that the IRQ numbers (aka INR's)
59 * are unique, the claim functions can always return IRQ_ACCEPT.
60 *
61 *
62 * Note about the irq_hash_table.
63 *
64 * The hash table is configured to use two keys: inr and devno.
65 * However, the hash index is computed only from inr. Moreover,
66 * if devno is -1, the match is based on the return value of
67 * the claim() function instead of on devno.
68 */
69
70#include <ddi/irq.h>
71#include <adt/hash_table.h>
72#include <mm/slab.h>
73#include <typedefs.h>
74#include <synch/spinlock.h>
75#include <console/console.h>
76#include <interrupt.h>
77#include <memstr.h>
78#include <arch.h>
79
80#define KEY_INR 0
81#define KEY_DEVNO 1
82
83/** Spinlock protecting the kernel IRQ hash table.
84 *
85 * This lock must be taken only when interrupts are disabled.
86 *
87 */
88IRQ_SPINLOCK_STATIC_INITIALIZE(irq_kernel_hash_table_lock);
89
90/** The kernel IRQ hash table. */
91static hash_table_t irq_kernel_hash_table;
92
93/** Spinlock protecting the uspace IRQ hash table.
94 *
95 * This lock must be taken only when interrupts are disabled.
96 *
97 */
98IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock);
99
100/** The uspace IRQ hash table. */
101hash_table_t irq_uspace_hash_table;
102
103/**
104 * Hash table operations for cases when we know that
105 * there will be collisions between different keys.
106 *
107 */
108static size_t irq_ht_hash(sysarg_t *key);
109static bool irq_ht_compare(sysarg_t *key, size_t keys, link_t *item);
110static void irq_ht_remove(link_t *item);
111
112static hash_table_operations_t irq_ht_ops = {
113 .hash = irq_ht_hash,
114 .compare = irq_ht_compare,
115 .remove_callback = irq_ht_remove,
116};
117
118/**
119 * Hash table operations for cases when we know that
120 * there will be no collisions between different keys.
121 * However, there might be still collisions among
122 * elements with single key (sharing of one IRQ).
123 *
124 */
125static size_t irq_lin_hash(sysarg_t *key);
126static bool irq_lin_compare(sysarg_t *key, size_t keys, link_t *item);
127static void irq_lin_remove(link_t *item);
128
129static hash_table_operations_t irq_lin_ops = {
130 .hash = irq_lin_hash,
131 .compare = irq_lin_compare,
132 .remove_callback = irq_lin_remove,
133};
134
135/** Number of buckets in either of the hash tables. */
136static size_t buckets;
137
138/** Last valid INR. */
139inr_t last_inr = 0;
140
141/** Initialize IRQ subsystem.
142 *
143 * @param inrs Numbers of unique IRQ numbers or INRs.
144 * @param chains Number of chains in the hash table.
145 *
146 */
147void irq_init(size_t inrs, size_t chains)
148{
149 buckets = chains;
150 last_inr = inrs - 1;
151
152 /*
153 * Be smart about the choice of the hash table operations.
154 * In cases in which inrs equals the requested number of
155 * chains (i.e. where there is no collision between
156 * different keys), we can use optimized set of operations.
157 */
158 if (inrs == chains) {
159 hash_table_create(&irq_uspace_hash_table, chains, 2,
160 &irq_lin_ops);
161 hash_table_create(&irq_kernel_hash_table, chains, 2,
162 &irq_lin_ops);
163 } else {
164 hash_table_create(&irq_uspace_hash_table, chains, 2,
165 &irq_ht_ops);
166 hash_table_create(&irq_kernel_hash_table, chains, 2,
167 &irq_ht_ops);
168 }
169}
170
171/** Initialize one IRQ structure.
172 *
173 * @param irq Pointer to the IRQ structure to be initialized.
174 *
175 */
176void irq_initialize(irq_t *irq)
177{
178 memsetb(irq, sizeof(irq_t), 0);
179 link_initialize(&irq->link);
180 irq_spinlock_initialize(&irq->lock, "irq.lock");
181 link_initialize(&irq->notif_cfg.link);
182 irq->inr = -1;
183 irq->devno = -1;
184
185 irq_initialize_arch(irq);
186}
187
188/** Register IRQ for device.
189 *
190 * The irq structure must be filled with information
191 * about the interrupt source and with the claim()
192 * function pointer and handler() function pointer.
193 *
194 * @param irq IRQ structure belonging to a device.
195 *
196 * @return True on success, false on failure.
197 *
198 */
199void irq_register(irq_t *irq)
200{
201 sysarg_t key[] = {
202 (sysarg_t) irq->inr,
203 (sysarg_t) irq->devno
204 };
205
206 irq_spinlock_lock(&irq_kernel_hash_table_lock, true);
207 irq_spinlock_lock(&irq->lock, false);
208 hash_table_insert(&irq_kernel_hash_table, key, &irq->link);
209 irq_spinlock_unlock(&irq->lock, false);
210 irq_spinlock_unlock(&irq_kernel_hash_table_lock, true);
211}
212
213/** Search and lock the uspace IRQ hash table.
214 *
215 */
216static irq_t *irq_dispatch_and_lock_uspace(inr_t inr)
217{
218 link_t *lnk;
219 sysarg_t key[] = {
220 (sysarg_t) inr,
221 (sysarg_t) -1 /* Search will use claim() instead of devno */
222 };
223
224 irq_spinlock_lock(&irq_uspace_hash_table_lock, false);
225 lnk = hash_table_find(&irq_uspace_hash_table, key);
226 if (lnk) {
227 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
228 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
229 return irq;
230 }
231 irq_spinlock_unlock(&irq_uspace_hash_table_lock, false);
232
233 return NULL;
234}
235
236/** Search and lock the kernel IRQ hash table.
237 *
238 */
239static irq_t *irq_dispatch_and_lock_kernel(inr_t inr)
240{
241 link_t *lnk;
242 sysarg_t key[] = {
243 (sysarg_t) inr,
244 (sysarg_t) -1 /* Search will use claim() instead of devno */
245 };
246
247 irq_spinlock_lock(&irq_kernel_hash_table_lock, false);
248 lnk = hash_table_find(&irq_kernel_hash_table, key);
249 if (lnk) {
250 irq_t *irq = hash_table_get_instance(lnk, irq_t, link);
251 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
252 return irq;
253 }
254 irq_spinlock_unlock(&irq_kernel_hash_table_lock, false);
255
256 return NULL;
257}
258
259/** Dispatch the IRQ.
260 *
261 * We assume this function is only called from interrupt
262 * context (i.e. that interrupts are disabled prior to
263 * this call).
264 *
265 * This function attempts to lookup a fitting IRQ
266 * structure. In case of success, return with interrupts
267 * disabled and holding the respective structure.
268 *
269 * @param inr Interrupt number (aka inr or irq).
270 *
271 * @return IRQ structure of the respective device or NULL.
272 *
273 */
274irq_t *irq_dispatch_and_lock(inr_t inr)
275{
276 /*
277 * If the kernel console override is on,
278 * then try first the kernel handlers
279 * and eventually fall back to uspace
280 * handlers.
281 *
282 * In the usual case the uspace handlers
283 * have precedence.
284 */
285
286 if (console_override) {
287 irq_t *irq = irq_dispatch_and_lock_kernel(inr);
288 if (irq)
289 return irq;
290
291 return irq_dispatch_and_lock_uspace(inr);
292 }
293
294 irq_t *irq = irq_dispatch_and_lock_uspace(inr);
295 if (irq)
296 return irq;
297
298 return irq_dispatch_and_lock_kernel(inr);
299}
300
301/** Compute hash index for the key.
302 *
303 * This function computes hash index into
304 * the IRQ hash table for which there
305 * can be collisions between different
306 * INRs.
307 *
308 * The devno is not used to compute the hash.
309 *
310 * @param key The first of the keys is inr and the second is devno or -1.
311 *
312 * @return Index into the hash table.
313 *
314 */
315size_t irq_ht_hash(sysarg_t key[])
316{
317 inr_t inr = (inr_t) key[KEY_INR];
318 return inr % buckets;
319}
320
321/** Compare hash table element with a key.
322 *
323 * There are two things to note about this function.
324 * First, it is used for the more complex architecture setup
325 * in which there are way too many interrupt numbers (i.e. inr's)
326 * to arrange the hash table so that collisions occur only
327 * among same inrs of different devnos. So the explicit check
328 * for inr match must be done.
329 * Second, if devno is -1, the second key (i.e. devno) is not
330 * used for the match and the result of the claim() function
331 * is used instead.
332 *
333 * This function assumes interrupts are already disabled.
334 *
335 * @param key Keys (i.e. inr and devno).
336 * @param keys This is 2.
337 * @param item The item to compare the key with.
338 *
339 * @return True on match or false otherwise.
340 *
341 */
342bool irq_ht_compare(sysarg_t key[], size_t keys, link_t *item)
343{
344 irq_t *irq = hash_table_get_instance(item, irq_t, link);
345 inr_t inr = (inr_t) key[KEY_INR];
346 devno_t devno = (devno_t) key[KEY_DEVNO];
347
348 bool rv;
349
350 irq_spinlock_lock(&irq->lock, false);
351 if (devno == -1) {
352 /* Invoked by irq_dispatch_and_lock(). */
353 rv = ((irq->inr == inr) &&
354 (irq->claim(irq) == IRQ_ACCEPT));
355 } else {
356 /* Invoked by irq_find_and_lock(). */
357 rv = ((irq->inr == inr) && (irq->devno == devno));
358 }
359
360 /* unlock only on non-match */
361 if (!rv)
362 irq_spinlock_unlock(&irq->lock, false);
363
364 return rv;
365}
366
367/** Unlock IRQ structure after hash_table_remove().
368 *
369 * @param lnk Link in the removed and locked IRQ structure.
370 */
371void irq_ht_remove(link_t *lnk)
372{
373 irq_t *irq __attribute__((unused))
374 = hash_table_get_instance(lnk, irq_t, link);
375 irq_spinlock_unlock(&irq->lock, false);
376}
377
378/** Compute hash index for the key.
379 *
380 * This function computes hash index into
381 * the IRQ hash table for which there
382 * are no collisions between different
383 * INRs.
384 *
385 * @param key The first of the keys is inr and the second is devno or -1.
386 *
387 * @return Index into the hash table.
388 *
389 */
390size_t irq_lin_hash(sysarg_t key[])
391{
392 inr_t inr = (inr_t) key[KEY_INR];
393 return inr;
394}
395
396/** Compare hash table element with a key.
397 *
398 * There are two things to note about this function.
399 * First, it is used for the less complex architecture setup
400 * in which there are not too many interrupt numbers (i.e. inr's)
401 * to arrange the hash table so that collisions occur only
402 * among same inrs of different devnos. So the explicit check
403 * for inr match is not done.
404 * Second, if devno is -1, the second key (i.e. devno) is not
405 * used for the match and the result of the claim() function
406 * is used instead.
407 *
408 * This function assumes interrupts are already disabled.
409 *
410 * @param key Keys (i.e. inr and devno).
411 * @param keys This is 2.
412 * @param item The item to compare the key with.
413 *
414 * @return True on match or false otherwise.
415 *
416 */
417bool irq_lin_compare(sysarg_t key[], size_t keys, link_t *item)
418{
419 irq_t *irq = list_get_instance(item, irq_t, link);
420 devno_t devno = (devno_t) key[KEY_DEVNO];
421 bool rv;
422
423 irq_spinlock_lock(&irq->lock, false);
424 if (devno == -1) {
425 /* Invoked by irq_dispatch_and_lock() */
426 rv = (irq->claim(irq) == IRQ_ACCEPT);
427 } else {
428 /* Invoked by irq_find_and_lock() */
429 rv = (irq->devno == devno);
430 }
431
432 /* unlock only on non-match */
433 if (!rv)
434 irq_spinlock_unlock(&irq->lock, false);
435
436 return rv;
437}
438
439/** Unlock IRQ structure after hash_table_remove().
440 *
441 * @param lnk Link in the removed and locked IRQ structure.
442 *
443 */
444void irq_lin_remove(link_t *lnk)
445{
446 irq_t *irq __attribute__((unused))
447 = hash_table_get_instance(lnk, irq_t, link);
448 irq_spinlock_unlock(&irq->lock, false);
449}
450
451/** @}
452 */
Note: See TracBrowser for help on using the repository browser.