2 * Copyright (c) 2006 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 /** @addtogroup genericddi
34 * @brief IRQ dispatcher
36 * This file provides means of connecting IRQs with respective device drivers
37 * and logic for dispatching interrupts to IRQ handlers defined by those
42 #include <adt/hash_table.h>
45 #include <synch/spinlock.h>
46 #include <console/console.h>
47 #include <interrupt.h>
51 /** Spinlock protecting the kernel IRQ hash table
53 * This lock must be taken only when interrupts are disabled.
56 IRQ_SPINLOCK_STATIC_INITIALIZE(irq_kernel_hash_table_lock
);
58 /** The kernel IRQ hash table. */
59 static hash_table_t irq_kernel_hash_table
;
61 /** Spinlock protecting the uspace IRQ hash table
63 * This lock must be taken only when interrupts are disabled.
66 IRQ_SPINLOCK_INITIALIZE(irq_uspace_hash_table_lock
);
68 /** The uspace IRQ hash table */
69 hash_table_t irq_uspace_hash_table
;
71 static size_t irq_ht_hash(sysarg_t
*key
);
72 static bool irq_ht_compare(sysarg_t
*key
, size_t keys
, link_t
*item
);
73 static void irq_ht_remove(link_t
*item
);
75 static hash_table_operations_t irq_ht_ops
= {
77 .compare
= irq_ht_compare
,
78 .remove_callback
= irq_ht_remove
,
81 /** Number of buckets in either of the hash tables */
82 static size_t buckets
;
87 /** Initialize IRQ subsystem
89 * @param inrs Numbers of unique IRQ numbers or INRs.
90 * @param chains Number of buckets in the hash table.
93 void irq_init(size_t inrs
, size_t chains
)
98 hash_table_create(&irq_uspace_hash_table
, chains
, 2, &irq_ht_ops
);
99 hash_table_create(&irq_kernel_hash_table
, chains
, 2, &irq_ht_ops
);
102 /** Initialize one IRQ structure
104 * @param irq Pointer to the IRQ structure to be initialized.
107 void irq_initialize(irq_t
*irq
)
109 memsetb(irq
, sizeof(irq_t
), 0);
110 link_initialize(&irq
->link
);
111 irq_spinlock_initialize(&irq
->lock
, "irq.lock");
112 link_initialize(&irq
->notif_cfg
.link
);
115 irq_initialize_arch(irq
);
118 /** Register IRQ for device
120 * The irq structure must be filled with information about the interrupt source
121 * and with the claim() function pointer and handler() function pointer.
123 * @param irq IRQ structure belonging to a device.
126 void irq_register(irq_t
*irq
)
129 [IRQ_HT_KEY_INR
] = (sysarg_t
) irq
->inr
,
130 [IRQ_HT_KEY_MODE
] = (sysarg_t
) IRQ_HT_MODE_NO_CLAIM
133 irq_spinlock_lock(&irq_kernel_hash_table_lock
, true);
134 irq_spinlock_lock(&irq
->lock
, false);
135 hash_table_insert(&irq_kernel_hash_table
, key
, &irq
->link
);
136 irq_spinlock_unlock(&irq
->lock
, false);
137 irq_spinlock_unlock(&irq_kernel_hash_table_lock
, true);
140 /** Search and lock the uspace IRQ hash table */
141 static irq_t
*irq_dispatch_and_lock_uspace(inr_t inr
)
145 [IRQ_HT_KEY_INR
] = (sysarg_t
) inr
,
146 [IRQ_HT_KEY_MODE
] = (sysarg_t
) IRQ_HT_MODE_CLAIM
149 irq_spinlock_lock(&irq_uspace_hash_table_lock
, false);
150 lnk
= hash_table_find(&irq_uspace_hash_table
, key
);
152 irq_t
*irq
= hash_table_get_instance(lnk
, irq_t
, link
);
153 irq_spinlock_unlock(&irq_uspace_hash_table_lock
, false);
156 irq_spinlock_unlock(&irq_uspace_hash_table_lock
, false);
161 /** Search and lock the kernel IRQ hash table */
162 static irq_t
*irq_dispatch_and_lock_kernel(inr_t inr
)
166 [IRQ_HT_KEY_INR
] = (sysarg_t
) inr
,
167 [IRQ_HT_KEY_MODE
] = (sysarg_t
) IRQ_HT_MODE_CLAIM
170 irq_spinlock_lock(&irq_kernel_hash_table_lock
, false);
171 lnk
= hash_table_find(&irq_kernel_hash_table
, key
);
173 irq_t
*irq
= hash_table_get_instance(lnk
, irq_t
, link
);
174 irq_spinlock_unlock(&irq_kernel_hash_table_lock
, false);
177 irq_spinlock_unlock(&irq_kernel_hash_table_lock
, false);
184 * We assume this function is only called from interrupt context (i.e. that
185 * interrupts are disabled prior to this call).
187 * This function attempts to lookup a fitting IRQ structure. In case of success,
188 * return with interrupts disabled and holding the respective structure.
190 * @param inr Interrupt number (aka inr or irq).
192 * @return IRQ structure of the respective device
193 * @return NULL if no IRQ structure found
196 irq_t
*irq_dispatch_and_lock(inr_t inr
)
199 * If the kernel console override is on, then try first the kernel
200 * handlers and eventually fall back to uspace handlers.
202 * In the usual case the uspace handlers have precedence.
205 if (console_override
) {
206 irq_t
*irq
= irq_dispatch_and_lock_kernel(inr
);
210 return irq_dispatch_and_lock_uspace(inr
);
213 irq_t
*irq
= irq_dispatch_and_lock_uspace(inr
);
217 return irq_dispatch_and_lock_kernel(inr
);
220 /** Compute hash index for the key
222 * @param key The first of the keys is inr and the second is mode. Only inr is
223 * used to compute the hash.
225 * @return Index into the hash table.
228 size_t irq_ht_hash(sysarg_t key
[])
230 inr_t inr
= (inr_t
) key
[IRQ_HT_KEY_INR
];
231 return inr
% buckets
;
234 /** Compare hash table element with a key
236 * If mode is IRQ_HT_MODE_CLAIM, the result of the claim() function is used for
237 * the match. Otherwise the key does not match.
239 * This function assumes interrupts are already disabled.
241 * @param key Keys (i.e. inr and mode).
242 * @param keys This is 2.
243 * @param item The item to compare the key with.
245 * @return True on match
246 * @return False on no match
249 bool irq_ht_compare(sysarg_t key
[], size_t keys
, link_t
*item
)
251 irq_t
*irq
= hash_table_get_instance(item
, irq_t
, link
);
252 inr_t inr
= (inr_t
) key
[IRQ_HT_KEY_INR
];
253 irq_ht_mode_t mode
= (irq_ht_mode_t
) key
[IRQ_HT_KEY_MODE
];
257 irq_spinlock_lock(&irq
->lock
, false);
258 if (mode
== IRQ_HT_MODE_CLAIM
) {
259 /* Invoked by irq_dispatch_and_lock(). */
260 rv
= ((irq
->inr
== inr
) && (irq
->claim(irq
) == IRQ_ACCEPT
));
262 /* Invoked by irq_find_and_lock(). */
266 /* unlock only on non-match */
268 irq_spinlock_unlock(&irq
->lock
, false);
273 /** Unlock IRQ structure after hash_table_remove()
275 * @param lnk Link in the removed and locked IRQ structure.
277 void irq_ht_remove(link_t
*lnk
)
279 irq_t
*irq
__attribute__((unused
))
280 = hash_table_get_instance(lnk
, irq_t
, link
);
281 irq_spinlock_unlock(&irq
->lock
, false);